-
Notifications
You must be signed in to change notification settings - Fork 50
feat(Unleash): add Unleash provider #652
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
Open
PSanetra
wants to merge
17
commits into
open-feature:main
Choose a base branch
from
PSanetra:feat/add-unleash-provider
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,303
−1
Open
Changes from 14 commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
514798a
feat(Unleash): add Unleash provider
PSanetra 3b4c7f0
feat(Unleash): use file bootstrap for tests, emit ConfigurationChange…
PSanetra 5a2b943
feat(Unleash): expose payload-type as flag metadata in resolution det…
PSanetra 9bca632
ci: add Unleash provider to release-please configuration
PSanetra b2a625b
docs(Unleash): add version.txt and README.md
PSanetra 1de0631
test(Unleash): remove workaround
PSanetra ee1618b
refactor(Unleash): rename OpenFeature.Contrib.Providers.Unleash to Op…
PSanetra a9f8674
Merge branch 'main' into feat/add-unleash-provider
lukas-reining 5f833a6
refactor(Unleash): address PR review feedback and improve initializat…
PSanetra a39700d
fix(Unleash): return ResolutionDetails with TypeMismatch error instea…
PSanetra a5cad57
fix(Unleash): return Reason.Disabled when variant is DISABLED_VARIANT
PSanetra 72bbfe7
refactor(Unleash): remove baseline context storage and merge logic
PSanetra 8d3b727
docs(Unleash): add README.md to OpenFeature.Providers.Unleash.csproj
PSanetra a69c1b6
chore(Unleash): add psanetra as component owner
PSanetra f90a1db
fix(Unleash): clear client reference on shutdown to prevent use-after…
PSanetra 0ab267a
feat(Unleash): honor cancellation tokens in resolve and shutdown methods
PSanetra 9765d96
Merge remote-tracking branch 'origin/main' into feat/add-unleash-prov…
PSanetra File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
106 changes: 106 additions & 0 deletions
106
src/OpenFeature.Providers.Unleash/EvaluationContextExtensions.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using OpenFeature.Model; | ||
| using Unleash; | ||
|
|
||
| namespace OpenFeature.Providers.Unleash; | ||
|
|
||
| /// <summary> | ||
| /// Extension methods for transforming an OpenFeature EvaluationContext into an Unleash UnleashContext. | ||
| /// </summary> | ||
| internal static class EvaluationContextExtensions | ||
| { | ||
| private const string UserIdKey = "userId"; | ||
| private const string SessionIdKey = "sessionId"; | ||
| private const string RemoteAddressKey = "remoteAddress"; | ||
| private const string EnvironmentKey = "environment"; | ||
| private const string AppNameKey = "appName"; | ||
| private const string CurrentTimeKey = "currentTime"; | ||
|
|
||
| /// <summary> | ||
| /// Gets the appName value from the evaluation context, or null if not present. | ||
| /// </summary> | ||
| /// <param name="context">The evaluation context.</param> | ||
| /// <returns>The appName value, or null.</returns> | ||
| public static string? GetAppName(this EvaluationContext? context) | ||
| { | ||
| if (context == null) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| return context.TryGetValue(AppNameKey, out var value) ? value?.AsString : null; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Transforms an OpenFeature EvaluationContext into an Unleash UnleashContext. | ||
| /// </summary> | ||
| /// <param name="context">The evaluation context, may be null.</param> | ||
| /// <returns>A new UnleashContext populated from the context.</returns> | ||
| public static UnleashContext ToUnleashContext(this EvaluationContext? context) | ||
| { | ||
| if (context == null) | ||
| { | ||
| return new UnleashContext(); | ||
| } | ||
|
|
||
| string? userId = null; | ||
| string? sessionId = null; | ||
| string? remoteAddress = null; | ||
| string? environment = null; | ||
| string? appName = null; | ||
| DateTimeOffset? currentTime = null; | ||
| var properties = new Dictionary<string, string>(); | ||
|
|
||
| if (!string.IsNullOrWhiteSpace(context.TargetingKey)) | ||
| { | ||
| userId = context.TargetingKey; | ||
| } | ||
|
|
||
| foreach (var kvp in context) | ||
| { | ||
| var key = kvp.Key; | ||
| var value = kvp.Value; | ||
|
|
||
| if (value == null) | ||
| { | ||
| continue; | ||
| } | ||
|
|
||
| var stringValue = value.AsString; | ||
|
|
||
| switch (key) | ||
| { | ||
| case UserIdKey: | ||
| userId = stringValue; | ||
| break; | ||
| case SessionIdKey: | ||
| sessionId = stringValue; | ||
| break; | ||
| case RemoteAddressKey: | ||
| remoteAddress = stringValue; | ||
| break; | ||
| case EnvironmentKey: | ||
| environment = stringValue; | ||
| break; | ||
| case AppNameKey: | ||
| appName = stringValue; | ||
| break; | ||
| case CurrentTimeKey: | ||
| if (DateTimeOffset.TryParse(stringValue, out var parsed)) | ||
| { | ||
| currentTime = parsed; | ||
| } | ||
| break; | ||
| default: | ||
| if (stringValue != null) | ||
| { | ||
| properties[key] = stringValue; | ||
| } | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| return new UnleashContext(appName, environment, userId, sessionId, remoteAddress, currentTime, properties); | ||
| } | ||
| } |
37 changes: 37 additions & 0 deletions
37
src/OpenFeature.Providers.Unleash/OpenFeature.Providers.Unleash.csproj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <PackageId>OpenFeature.Providers.Unleash</PackageId> | ||
| <VersionNumber>0.1.0</VersionNumber> <!--x-release-please-version --> | ||
| <VersionPrefix>$(VersionNumber)</VersionPrefix> | ||
| <AssemblyVersion>$(VersionNumber)</AssemblyVersion> | ||
| <FileVersion>$(VersionNumber)</FileVersion> | ||
| <Description>Unleash provider for OpenFeature .NET SDK</Description> | ||
| <Authors>OpenFeature Authors</Authors> | ||
| <GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
| <PackageReadmeFile>README.md</PackageReadmeFile> | ||
| <RootNamespace>OpenFeature.Providers.Unleash</RootNamespace> | ||
| <Nullable>enable</Nullable> | ||
| </PropertyGroup> | ||
|
|
||
| <!-- documentation --> | ||
| <ItemGroup> | ||
| <None Include="README.md" Pack="true" PackagePath="\" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <!-- make the internal methods visible to our test project --> | ||
| <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo"> | ||
| <_Parameter1>$(MSBuildProjectName).Test</_Parameter1> | ||
| </AssemblyAttribute> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Unleash.Client" Version="[6.2.1,7.0)" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="OpenFeature" Version="[2.0,3.0)" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| # Unleash .NET Provider | ||
|
|
||
| The Unleash provider allows you to use [Unleash](https://www.getunleash.io/) with the OpenFeature .NET SDK. | ||
|
|
||
| # .Net SDK usage | ||
|
|
||
| ## Requirements | ||
|
|
||
| - open-feature/dotnet-sdk v2.x | ||
| - Unleash .NET SDK v6.x | ||
|
|
||
| ## Install dependencies | ||
|
|
||
| The first thing we will do is install the **OpenFeature SDK** and the **Unleash Feature Flag provider**. | ||
|
|
||
| ### .NET Cli | ||
|
|
||
| ```shell | ||
| dotnet add package OpenFeature.Providers.Unleash | ||
| ``` | ||
|
|
||
| ### Package Manager | ||
|
|
||
| ```shell | ||
| NuGet\Install-Package OpenFeature.Providers.Unleash | ||
| ``` | ||
|
|
||
| ### Package Reference | ||
|
|
||
| ```xml | ||
| <PackageReference Include="OpenFeature.Providers.Unleash" /> | ||
| ``` | ||
|
|
||
| ### Packet cli | ||
|
|
||
| ```shell | ||
| paket add OpenFeature.Providers.Unleash | ||
| ``` | ||
|
|
||
| ### Cake | ||
|
|
||
| ```shell | ||
| // Install OpenFeature.Providers.Unleash as a Cake Addin | ||
| #addin nuget:?package=OpenFeature.Providers.Unleash | ||
|
|
||
| // Install OpenFeature.Providers.Unleash as a Cake Tool | ||
| #tool nuget:?package=OpenFeature.Providers.Unleash | ||
| ``` | ||
|
|
||
| ## Using the Unleash Provider with the OpenFeature SDK | ||
|
|
||
| ```csharp | ||
| using OpenFeature; | ||
| using OpenFeature.Providers.Unleash; | ||
| using Unleash; | ||
|
|
||
| var settings = new UnleashSettings | ||
| { | ||
| AppName = "my-app", | ||
| UnleashApi = new Uri("http://localhost:4242/api/"), | ||
| CustomHttpHeaders = new Dictionary<string, string> | ||
| { | ||
| { "Authorization", "*:development.your-api-token" } | ||
| } | ||
| }; | ||
|
|
||
| var provider = new UnleashProvider(settings); | ||
|
|
||
| // Set the provider for the OpenFeature SDK | ||
| await Api.Instance.SetProviderAsync(provider); | ||
|
|
||
| // Get an OpenFeature client | ||
| var client = Api.Instance.GetClient(); | ||
|
|
||
| // Boolean evaluation (uses IsEnabled) | ||
| var enabled = await client.GetBooleanValueAsync("my-feature", false); | ||
|
|
||
| // String evaluation (uses variant payload) | ||
| var value = await client.GetStringValueAsync("my-variant-flag", "default"); | ||
|
|
||
| // Integer evaluation (parses variant payload) | ||
| var count = await client.GetIntegerValueAsync("my-int-flag", 0); | ||
|
|
||
| // Double evaluation (parses variant payload) | ||
| var rate = await client.GetDoubleValueAsync("my-double-flag", 0.0); | ||
| ``` | ||
|
|
||
| ## EvaluationContext and Unleash Context relationship | ||
|
|
||
| The provider maps OpenFeature `EvaluationContext` fields to `UnleashContext`: | ||
|
|
||
| | EvaluationContext Key | Unleash Context Field | | ||
| |-----------------------|------------------------| | ||
| | `TargetingKey` | `UserId` | | ||
| | `sessionId` | `SessionId` | | ||
| | `remoteAddress` | `RemoteAddress` | | ||
| | `environment` | `Environment` | | ||
| | `appName` | `AppName` | | ||
| | `currentTime` | `CurrentTime` | | ||
| | All other keys | `Properties` | | ||
|
|
||
| ## Variant payload type metadata | ||
|
|
||
| When evaluating variants (string, integer, double, structure), the provider exposes the Unleash payload `type` field (e.g., `"string"`, `"number"`, `"json"`, `"csv"`) as `payload-type` in the resolution details flag metadata. | ||
|
|
||
| ## Events | ||
|
|
||
| The provider emits `ProviderConfigurationChanged` events when Unleash fires `TogglesUpdatedEvent` (i.e., when toggle state is refreshed from the server). | ||
|
|
||
| ## Known issues and limitations | ||
|
|
||
| - The provider does not accept an external `IUnleash` instance because lifecycle events (`ReadyEvent`, `ErrorEvent`, `TogglesUpdatedEvent`) can only be subscribed during client construction. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.