Skip to content
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

feat: Introduce flipt provider for dotnet #285

Merged
merged 43 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
97a5970
Initial working commit without tests
jeandreidc Sep 26, 2024
c707b3e
FliptProvider: Handle type mismatch and Value casting
jeandreidc Sep 27, 2024
ee902c6
FliptProvider: Added extension to map flipt context to openFeature, a…
jeandreidc Sep 27, 2024
656711b
Added xmldoc on extension function
jeandreidc Sep 27, 2024
f17949b
Added handling of different Value types, added tests to check for pro…
jeandreidc Sep 27, 2024
7898c86
Added handling of array of Values, introduced universal deserializer …
jeandreidc Sep 27, 2024
41cf5bf
Added support to serialize nested Values list
jeandreidc Sep 27, 2024
d94a6c6
FliptProvider: Add support for serializing structure
jeandreidc Sep 27, 2024
d651b3c
FliptProvider: Implement Structure deserializer
jeandreidc Sep 27, 2024
77afc27
FliptProvider: Rename ClientWrapper to converter
jeandreidc Sep 30, 2024
a9ca17f
FliptProvider: Implemented wrapper class, added unit tests
jeandreidc Sep 30, 2024
d98f9ce
FliptProvider: Code cleanup
jeandreidc Sep 30, 2024
48c555c
FliptProvider: Extracted Converter tests to its own file
jeandreidc Oct 1, 2024
d4b3e42
Added unit tests for FliptToOpenFeature converter for variants and bo…
jeandreidc Oct 2, 2024
10b1d86
Added FliptToOpenFeatureConverter tests for non-existent flags for Va…
jeandreidc Oct 2, 2024
d8d382d
Update release metadata
jeandreidc Oct 2, 2024
204144f
Replaced Flipt package with a generated RestClient from their openapi…
jeandreidc Oct 2, 2024
563dd5f
Changed exception handling to use generated exceptions by nswag
jeandreidc Oct 2, 2024
29077ea
Modified unit test to throw mocked FliptExceptions
jeandreidc Oct 2, 2024
e11bdaa
Utilized OpenFeature Reason type on Resolutions
jeandreidc Oct 2, 2024
cd1b5a9
Change test project setting; Cleaned FLiptConverter code
jeandreidc Oct 3, 2024
83d0fde
Change types to support all .net target versions
jeandreidc Oct 3, 2024
2417a9a
FliptProvider: Update Readme
jeandreidc Oct 3, 2024
fd7210a
FliptProvider: Update ReadMe, test fliptClientWrapper calls
jeandreidc Oct 3, 2024
328c5a1
FliptProvider: Removed wrong code change in a different project
jeandreidc Oct 3, 2024
0dcca7e
lint
jeandreidc Oct 3, 2024
cc8a209
Used HttpStatusCode.Ambiguous because TooManyRequests is not yet impl…
jeandreidc Oct 3, 2024
c3d28b8
FliptProvider: Updated openapi.yaml src, regenerated RestClient using…
jeandreidc Oct 3, 2024
6633132
Update readme instructions
jeandreidc Oct 4, 2024
d8c4e85
reverted nuget.config changes, fix linting spacing issue caused by fo…
jeandreidc Oct 4, 2024
dfb13ee
Merge remote-tracking branch 'upstream/main' into feature/flipt-clien…
jeandreidc Oct 8, 2024
a5ed946
FliptProvider: Updated to throw exceptions instead of handling them a…
jeandreidc Oct 8, 2024
81b5162
FliptProvider: Moved client class generation and referencing in build…
jeandreidc Oct 8, 2024
128c142
FliptProvider: Made constructor that receives a dependent interface i…
jeandreidc Oct 8, 2024
05944bf
FliptProvider: Updated component_owners
jeandreidc Oct 8, 2024
d491ef3
Update readme
jeandreidc Oct 8, 2024
d17d2d8
Merge remote-tracking branch 'upstream/main' into feature/flipt-clien…
jeandreidc Oct 9, 2024
2110460
Reverted unintended lint changes to Flagsmith.csproj
jeandreidc Oct 10, 2024
062445a
Fixed Nswag build time error, updated README, removed openapi.yaml cu…
jeandreidc Oct 10, 2024
14a2223
lint changes in test
jeandreidc Oct 10, 2024
3224b95
Merge remote-tracking branch 'upstream/main' into feature/flipt-clien…
jeandreidc Oct 10, 2024
582a4fb
Updated System.Text.Json to latest version
jeandreidc Oct 10, 2024
09dbc34
Changed prebuild stage of nswag generation script
jeandreidc Oct 14, 2024
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
6 changes: 6 additions & 0 deletions .github/component_owners.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ components:
src/OpenFeature.Contrib.Providers.Statsig:
- jenshenneberg
- lattenborough
src/OpenFeature.Contrib.Providers.Flipt:
- jeandreidc
- markphelps

# test/
test/OpenFeature.Contrib.Hooks.Otel.Test:
Expand All @@ -45,6 +48,9 @@ components:
test/src/OpenFeature.Contrib.Providers.Statsig.Test:
- jenshenneberg
- lattenborough
test/src/OpenFeature.Contrib.Providers.Flipt.Test:
- jeandreidc
- markphelps

ignored-authors:
- renovate-bot
3 changes: 2 additions & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
"src/OpenFeature.Contrib.Providers.Flagsmith": "0.2.0",
"src/OpenFeature.Contrib.Providers.ConfigCat": "0.1.1",
"src/OpenFeature.Contrib.Providers.FeatureManagement": "0.1.0",
"src/OpenFeature.Contrib.Providers.Statsig": "0.1.0"
"src/OpenFeature.Contrib.Providers.Statsig": "0.1.0",
"src/OpenFeature.Contrib.Providers.Flipt": "0.0.1"
}
14 changes: 14 additions & 0 deletions DotnetSdkContrib.sln
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenFeature.Contrib.Provide
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenFeature.Contrib.Providers.Statsig.Test", "test\OpenFeature.Contrib.Providers.Statsig.Test\OpenFeature.Contrib.Providers.Statsig.Test.csproj", "{F3080350-B0AB-4D59-B416-50CC38C99087}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenFeature.Contrib.Providers.Flipt", "src\OpenFeature.Contrib.Providers.Flipt\OpenFeature.Contrib.Providers.Flipt.csproj", "{5ECF7DBF-FE64-40A2-BF39-239DE173DA4B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenFeature.Contrib.Providers.Flipt.Test", "test\OpenFeature.Contrib.Providers.Flipt.Test\OpenFeature.Contrib.Providers.Flipt.Test.csproj", "{B446D481-B5A3-4509-8933-C4CF6DA9B147}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -115,6 +119,14 @@ Global
{F3080350-B0AB-4D59-B416-50CC38C99087}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F3080350-B0AB-4D59-B416-50CC38C99087}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F3080350-B0AB-4D59-B416-50CC38C99087}.Release|Any CPU.Build.0 = Release|Any CPU
{5ECF7DBF-FE64-40A2-BF39-239DE173DA4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5ECF7DBF-FE64-40A2-BF39-239DE173DA4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5ECF7DBF-FE64-40A2-BF39-239DE173DA4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5ECF7DBF-FE64-40A2-BF39-239DE173DA4B}.Release|Any CPU.Build.0 = Release|Any CPU
{B446D481-B5A3-4509-8933-C4CF6DA9B147}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B446D481-B5A3-4509-8933-C4CF6DA9B147}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B446D481-B5A3-4509-8933-C4CF6DA9B147}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B446D481-B5A3-4509-8933-C4CF6DA9B147}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -137,5 +149,7 @@ Global
{B8C5376B-BAFE-48B8-ABC1-111A93C033F2} = {B6D3230B-5E4D-4FF1-868E-2F4E325C84FE}
{4C7C0E2D-6ECC-4D17-BC5D-18F6BA6F872A} = {0E563821-BD08-4B7F-BF9D-395CAD80F026}
{F3080350-B0AB-4D59-B416-50CC38C99087} = {B6D3230B-5E4D-4FF1-868E-2F4E325C84FE}
{5ECF7DBF-FE64-40A2-BF39-239DE173DA4B} = {0E563821-BD08-4B7F-BF9D-395CAD80F026}
{B446D481-B5A3-4509-8933-C4CF6DA9B147} = {B6D3230B-5E4D-4FF1-868E-2F4E325C84FE}
EndGlobalSection
EndGlobal
4 changes: 2 additions & 2 deletions nuget.config
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>

<configuration>

<packageSources>
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
<add key="github-open-feature" value="https://nuget.pkg.github.com/open-feature/index.json" />
<add key="github-open-feature" value="https://nuget.pkg.github.com/open-feature/index.json"/>
</packageSources>

<packageSourceMapping>
Expand Down
10 changes: 10 additions & 0 deletions release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@
"extra-files": [
"OpenFeature.Contrib.Providers.Statsig.csproj"
]
},
"src/OpenFeature.Contrib.Providers.Flipt": {
"package-name": "OpenFeature.Contrib.Providers.Flipt",
"release-type": "simple",
"bump-minor-pre-major": true,
"bump-patch-for-minor-pre-major": true,
"versioning": "default",
"extra-files": [
"OpenFeature.Contrib.Providers.Flipt.csproj"
]
}
},
"changelog-sections": [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Flipt.Rest;

namespace OpenFeature.Contrib.Providers.Flipt.ClientWrapper;

/// <summary>
/// Wrapper for Flipt server sdk client for .net
/// </summary>
public class FliptClientWrapper : IFliptClientWrapper
{
private readonly FliptRestClient _fliptRestClient;

/// <summary>
/// </summary>
/// <param name="fliptUrl">Url of flipt instance</param>
/// <param name="clientToken">Authentication access token</param>
/// <param name="timeoutInSeconds">Timeout when calling flipt endpoints in seconds</param>
public FliptClientWrapper(string fliptUrl,
string clientToken = "",
int timeoutInSeconds = 30)
{
_fliptRestClient = BuildClient(fliptUrl, clientToken, timeoutInSeconds);
}

/// <inheritdoc />
public async Task<VariantEvaluationResponse> EvaluateVariantAsync(EvaluationRequest evaluationRequest)
{
return await _fliptRestClient.EvaluateV1VariantAsync(evaluationRequest);
}

/// <inheritdoc />
public async Task<BooleanEvaluationResponse> EvaluateBooleanAsync(EvaluationRequest evaluationRequest)
{
return await _fliptRestClient.EvaluateV1BooleanAsync(evaluationRequest);
}

private static FliptRestClient BuildClient(string fliptUrl, string clientToken, int timeoutInSeconds = 30)
{
var httpClient = new HttpClient
{
BaseAddress = new Uri(fliptUrl),
Timeout = TimeSpan.FromSeconds(timeoutInSeconds),
DefaultRequestHeaders = { { "Authorization", $"Bearer {clientToken}" } }
};
return new FliptRestClient(httpClient);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Threading.Tasks;
using Flipt.Rest;

namespace OpenFeature.Contrib.Providers.Flipt.ClientWrapper;

/// <summary>
/// </summary>
public interface IFliptClientWrapper
{
/// <summary>
/// Wrapper to Flipt.io/EvaluateVariantAsync method
/// </summary>
/// <param name="evaluationRequest"></param>
/// <returns></returns>
Task<VariantEvaluationResponse> EvaluateVariantAsync(EvaluationRequest evaluationRequest);

/// <summary>
/// Wrapper to Flipt.io/EvaluateBooleanAsync method
/// </summary>
/// <param name="evaluationRequest"></param>
/// <returns></returns>
Task<BooleanEvaluationResponse> EvaluateBooleanAsync(EvaluationRequest evaluationRequest);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Text.Json;

namespace OpenFeature.Contrib.Providers.Flipt.Converters;

/// <summary>
/// Extensions for default JsonConverter behavior
/// </summary>
public static class JsonConverterExtensions
{
/// <summary>
/// JsonConverter serializer settings for Flipt to OpenFeature model deserialization
/// </summary>
public static readonly JsonSerializerOptions DefaultSerializerSettings = new()
{
WriteIndented = true,
AllowTrailingCommas = true,
Converters =
{
new OpenFeatureStructureConverter(),
new OpenFeatureValueConverter()
}
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
using OpenFeature.Model;

namespace OpenFeature.Contrib.Providers.Flipt.Converters;

/// <summary>
/// JsonConverter for OpenFeature Structure type
/// </summary>
public class OpenFeatureStructureConverter : JsonConverter<Structure>
{
/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, Structure value, JsonSerializerOptions options)
{
var jsonDoc = JsonDocument.Parse(JsonSerializer.Serialize(value.AsDictionary(),
JsonConverterExtensions.DefaultSerializerSettings));
jsonDoc.WriteTo(writer);
}

/// <inheritdoc />
public override Structure Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
using var jsonDocument = JsonDocument.ParseValue(ref reader);
var jsonText = jsonDocument.RootElement.GetRawText();
return new Structure(JsonSerializer.Deserialize<Dictionary<string, Value>>(jsonText,
JsonConverterExtensions.DefaultSerializerSettings));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
using OpenFeature.Model;

namespace OpenFeature.Contrib.Providers.Flipt.Converters;

/// <summary>
/// OpenFeature Value type converter
/// </summary>
public class OpenFeatureValueConverter : JsonConverter<Value>
{
/// <inheritdoc />
public override Value Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var value = new Value();
switch (reader.TokenType)
{
case JsonTokenType.String:
return reader.TryGetDateTime(out var dateTimeValue)
? new Value(dateTimeValue)
: new Value(reader.GetString() ?? string.Empty);
case JsonTokenType.True:
case JsonTokenType.False:
return new Value(reader.GetBoolean());
case JsonTokenType.Number:
if (reader.TryGetInt32(out var intValue)) return new Value(intValue);
if (reader.TryGetDouble(out var dblValue)) return new Value(dblValue);
break;
case JsonTokenType.StartArray:
return new Value(GenerateValueArray(ref reader, typeToConvert, options));
case JsonTokenType.StartObject:
return new Value(GetStructure(ref reader, typeToConvert, options));
}

return value;
}

private Structure GetStructure(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var startDepth = reader.CurrentDepth;
var structureDictionary = new Dictionary<string, Value>();
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.PropertyName)
{
var key = reader.GetString();
reader.Read();
var val = Read(ref reader, typeToConvert, options);
structureDictionary[key ?? string.Empty] = val;
}

if (reader.TokenType == JsonTokenType.EndObject && reader.CurrentDepth == startDepth) break;
}

return new Structure(structureDictionary);
}


private IList<Value> GenerateValueArray(ref Utf8JsonReader reader, Type typeToConvert,
JsonSerializerOptions options)
{
var valuesArray = new List<Value>();
var startDepth = reader.CurrentDepth;

while (reader.Read())
switch (reader.TokenType)
{
case JsonTokenType.EndArray when reader.CurrentDepth == startDepth:
return valuesArray;
default:
valuesArray.Add(Read(ref reader, typeToConvert, options));
break;
}

return valuesArray;
}

/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, Value value, JsonSerializerOptions options)
{
if (value.IsList)
{
writer.WriteStartArray();
foreach (var val in value.AsList!)
{
var jsonDoc = JsonDocument.Parse(JsonSerializer.Serialize(val.AsObject,
JsonConverterExtensions.DefaultSerializerSettings));
jsonDoc.WriteTo(writer);
}

writer.WriteEndArray();
}
else
{
var jsonDoc = JsonDocument.Parse(JsonSerializer.Serialize(value.AsObject,
JsonConverterExtensions.DefaultSerializerSettings));
jsonDoc.WriteTo(writer);
}
}
}
27 changes: 27 additions & 0 deletions src/OpenFeature.Contrib.Providers.Flipt/FliptExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using OpenFeature.Contrib.Providers.Flipt.Converters;
using OpenFeature.Model;

namespace OpenFeature.Contrib.Providers.Flipt;

/// <summary>
/// Extension helper methods
/// </summary>
public static class FliptExtensions
{
/// <summary>
/// Transforms openFeature EvaluationContext to a mutable Dictionary that flipt sdk accepts
/// </summary>
/// <param name="evaluationContext">OpenFeature EvaluationContext</param>
/// <returns></returns>
public static Dictionary<string, string> ToStringDictionary(this EvaluationContext evaluationContext)
{
return evaluationContext?.AsDictionary()
.ToDictionary(k => k.Key,
v => JsonSerializer.Serialize(v.Value.AsObject,
JsonConverterExtensions.DefaultSerializerSettings)) ??
[];
}
}
Loading
Loading