Skip to content

Commit

Permalink
Flagsmith improvements and start creating tests
Browse files Browse the repository at this point in the history
  • Loading branch information
vpetrusevici committed Oct 18, 2023
1 parent d1fb7eb commit 1a355ab
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 69 deletions.
9 changes: 8 additions & 1 deletion DotnetSdkContrib.sln
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenFeature.Contrib.Provide
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenFeature.Contrib.Providers.GOFeatureFlag.Test", "test\OpenFeature.Contrib.Providers.GOFeatureFlag.Test\OpenFeature.Contrib.Providers.GOFeatureFlag.Test.csproj", "{4041B63F-9CF6-4886-8FC7-BD1A7E45F859}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenFeature.Contrib.Providers.Flagsmith", "src\OpenFeature.Contrib.Providers.Flagsmith\OpenFeature.Contrib.Providers.Flagsmith.csproj", "{47008BEE-7888-4B9B-8884-712A922C3F9B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenFeature.Contrib.Providers.Flagsmith", "src\OpenFeature.Contrib.Providers.Flagsmith\OpenFeature.Contrib.Providers.Flagsmith.csproj", "{47008BEE-7888-4B9B-8884-712A922C3F9B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenFeature.Contrib.Providers.Flagsmith.Test", "test\OpenFeature.Contrib.Providers.Flagsmith.Test\OpenFeature.Contrib.Providers.Flagsmith.Test.csproj", "{C3BA23C2-BEC3-4683-A64A-C914C3D8037E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -55,6 +57,10 @@ Global
{47008BEE-7888-4B9B-8884-712A922C3F9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{47008BEE-7888-4B9B-8884-712A922C3F9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{47008BEE-7888-4B9B-8884-712A922C3F9B}.Release|Any CPU.Build.0 = Release|Any CPU
{C3BA23C2-BEC3-4683-A64A-C914C3D8037E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C3BA23C2-BEC3-4683-A64A-C914C3D8037E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C3BA23C2-BEC3-4683-A64A-C914C3D8037E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C3BA23C2-BEC3-4683-A64A-C914C3D8037E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -67,5 +73,6 @@ Global
{F7BE205B-0375-4EC5-9B18-FAFEF7A78D71} = {0E563821-BD08-4B7F-BF9D-395CAD80F026}
{4041B63F-9CF6-4886-8FC7-BD1A7E45F859} = {B6D3230B-5E4D-4FF1-868E-2F4E325C84FE}
{47008BEE-7888-4B9B-8884-712A922C3F9B} = {0E563821-BD08-4B7F-BF9D-395CAD80F026}
{C3BA23C2-BEC3-4683-A64A-C914C3D8037E} = {B6D3230B-5E4D-4FF1-868E-2F4E325C84FE}
EndGlobalSection
EndGlobal

This file was deleted.

95 changes: 48 additions & 47 deletions src/OpenFeature.Contrib.Providers.Flagsmith/FlagsmithProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OpenFeature.Constant;
using OpenFeature.Contrib.Providers.Flagsmith.Exceptions;
using OpenFeature.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using Trait = Flagsmith.Trait;

Expand All @@ -19,8 +18,8 @@ namespace OpenFeature.Contrib.Providers.Flagsmith
/// </summary>
public class FlagsmithProvider : FeatureProvider
{
private readonly static Metadata Metadata = new Metadata("Flagsmith Provider");
private readonly FlagsmithClient _flagsmithClient;
private readonly static Metadata Metadata = new("Flagsmith Provider");
internal readonly IFlagsmithClient _flagsmithClient;
/// <summary>
/// Creates new instance of <see cref="FlagsmithProvider"/>
/// </summary>
Expand All @@ -39,6 +38,17 @@ public FlagsmithProvider(IFlagsmithConfiguration options, HttpClient httpClient)
{
_flagsmithClient = new FlagsmithClient(options, httpClient);
}


/// <summary>
/// Creates new instance of <see cref="FlagsmithProvider"/>
/// </summary>
/// <param name="flagsmithClient">Precreated Flagsmith client. You can just use <see cref="FlagsmithClient"/> class.</param>
public FlagsmithProvider(IFlagsmithClient flagsmithClient)
{
_flagsmithClient = flagsmithClient;
}

private Task<IFlags> GetFlags(EvaluationContext ctx)
{
var key = ctx.GetValue("targetingKey").AsString;
Expand All @@ -63,9 +73,9 @@ public override async Task<ResolutionDetails<bool>> ResolveBooleanValue(string f
}

var stringValue = await flags.GetFeatureValue(flagKey);
if (bool.TryParse(stringValue, out var intValue))
if (bool.TryParse(stringValue, out var parsedValue))
{
return new ResolutionDetails<bool>(flagKey, intValue);
return new ResolutionDetails<bool>(flagKey, parsedValue);
}
return new ResolutionDetails<bool>(flagKey, defaultValue, ErrorType.ParseError, Reason.Error, errorMessage: "Failed to parse value in int type");

Expand Down Expand Up @@ -99,9 +109,9 @@ public override async Task<ResolutionDetails<int>> ResolveIntegerValue(string fl
}

var stringValue = await flags.GetFeatureValue(flagKey);
if(int.TryParse(stringValue, out var intValue))
if(int.TryParse(stringValue, out var parsedValue))
{
return new ResolutionDetails<int>(flagKey, intValue);
return new ResolutionDetails<int>(flagKey, parsedValue);
}
return new ResolutionDetails<int>(flagKey, defaultValue, ErrorType.ParseError, Reason.Error, errorMessage: "Failed to parse value in int type");
}
Expand All @@ -118,9 +128,9 @@ public override async Task<ResolutionDetails<double>> ResolveDoubleValue(string
}

var stringValue = await flags.GetFeatureValue(flagKey);
if (double.TryParse(stringValue, out var intValue))
if (double.TryParse(stringValue, out var parsedValue))
{
return new ResolutionDetails<double>(flagKey, intValue);
return new ResolutionDetails<double>(flagKey, parsedValue);
}
return new ResolutionDetails<double>(flagKey, defaultValue, ErrorType.ParseError, Reason.Error, errorMessage: "Failed to parse value in double type");
}
Expand All @@ -138,62 +148,53 @@ public override async Task<ResolutionDetails<Value>> ResolveStructureValue(strin
}
var stringValue = await flags.GetFeatureValue(flagKey);

var mappedValue = JsonConvert.DeserializeObject(stringValue);
if (mappedValue is JsonElement element)
var mappedValue = JsonNode.Parse(stringValue);
var value = ConvertValue(mappedValue);
if (value is null)
{
var value = ConvertValue(element);
return new ResolutionDetails<Value>(flagKey, value);
return new ResolutionDetails<Value>(flagKey, defaultValue, ErrorType.ParseError, Reason.Error, errorMessage: "Failed to parse value in structure type or value is null");
}
return new ResolutionDetails<Value>(flagKey, defaultValue, ErrorType.ParseError, Reason.Error, errorMessage: "Failed to parse value in structure type");
return new ResolutionDetails<Value>(flagKey, value);
}

/// <summary>
/// convertValue is converting the object return by the proxy response in the right type.
/// </summary>
/// <param name="value">The value we have received</param>
/// <param name="node">The value we have received</param>
/// <returns>A converted object</returns>
/// <exception cref="InvalidCastException">If we are not able to convert the data.</exception>
private Value ConvertValue(JsonElement value)
private Value ConvertValue(JsonNode node)
{
if (value.ValueKind == JsonValueKind.Null || value.ValueKind == JsonValueKind.Undefined) return null;

if (value.ValueKind == JsonValueKind.False || value.ValueKind == JsonValueKind.True)
return new Value(value.GetBoolean());

if (value.ValueKind == JsonValueKind.Number) return new Value(value.GetDouble());

if (value.ValueKind == JsonValueKind.Object)
if(node == null)
return null;
if (node is JsonArray jsonArray)
{
var dict = new Dictionary<string, Value>();
using var objEnumerator = value.EnumerateObject();
while (objEnumerator.MoveNext())
var arr = new List<Value>();
foreach (var item in jsonArray)
{
var current = objEnumerator.Current;
var currentValue = ConvertValue(current.Value);
if (currentValue != null) dict.Add(current.Name, ConvertValue(current.Value));
var convertedValue = ConvertValue(item);
if (convertedValue != null) arr.Add(convertedValue);
}
return new Value(arr);
}

if (node is JsonObject jsonObject)
{
var dict = jsonObject.ToDictionary(x => x.Key, x => ConvertValue(x.Value));

return new Value(new Structure(dict));
}

if (value.ValueKind == JsonValueKind.String) return new Value(value.ToString());

if (value.ValueKind == JsonValueKind.Array)
if (node.AsValue().TryGetValue<JsonElement>(out var jsonElement))
{
using var arrayEnumerator = value.EnumerateArray();
var arr = new List<Value>();
if (jsonElement.ValueKind == JsonValueKind.False || jsonElement.ValueKind == JsonValueKind.True)
return new Value(jsonElement.GetBoolean());
if (jsonElement.ValueKind == JsonValueKind.Number)
return new Value(jsonElement.GetDouble());

while (arrayEnumerator.MoveNext())
{
var current = arrayEnumerator.Current;
var convertedValue = ConvertValue(current);
if (convertedValue != null) arr.Add(convertedValue);
}

return new Value(arr);
if (jsonElement.ValueKind == JsonValueKind.String)
return new Value(jsonElement.ToString());
}

throw new FlagsmithProviderException($"impossible to convert the object {value}");
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard20</TargetFrameworks>
Expand Down Expand Up @@ -27,6 +27,6 @@
</ItemGroup>

<PropertyGroup>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
<ProjectReference Include="..\..\src\OpenFeature.Contrib.Hooks.Otel\OpenFeature.Contrib.Hooks.Otel.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="OpenTelemetry" Version="1.4.0" />
<PackageReference Include="OpenTelemetry.Exporter.InMemory" Version="1.4.0" />
<ItemGroup>
<PackageReference Include="OpenTelemetry" Version="1.4.0" />
<PackageReference Include="OpenTelemetry.Exporter.InMemory" Version="1.4.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using Xunit;
using System;
using Flagsmith;
using System.Net.Http;

namespace OpenFeature.Contrib.Providers.Flagsmith.Test
{
public class UnitTestFlagsmithProvider
{
private static FlagsmithConfiguration GetDefaultFlagsmithConfiguration() => new ()
{
ApiUrl = "https://edge.api.flagsmith.com/api/v1/",
EnvironmentKey = string.Empty,
EnableClientSideEvaluation = false,
EnvironmentRefreshIntervalSeconds = 60,
EnableAnalytics = false,
Retries = 1
};
[Fact]
public void CreateFlagmithProvider_WithValidCredetials_CreatesInstanceSuccessfully()
{
// Arrange
var config = GetDefaultFlagsmithConfiguration();

// Act
var flagsmithProvider = new FlagsmithProvider(config);


// Assert
Assert.NotNull(flagsmithProvider._flagsmithClient);
}

[Fact]
public void CreateFlagmithProvider_WithValidCredetialsAndCustomHttpClient_CreatesInstanceSuccessfully()
{
// Arrange
var config = GetDefaultFlagsmithConfiguration();
using var httpClient = new HttpClient();
// Act
var flagsmithProvider = new FlagsmithProvider(config, httpClient);


// Assert
Assert.NotNull(flagsmithProvider._flagsmithClient);
}

[Fact]
public void CreateFlagmithProvider_WithCustomFlagsmithClient_CreatesInstanceSuccessfully()
{
// Arrange
var flagsmithClient = new FlagsmithClient(GetDefaultFlagsmithConfiguration());
// Act
var flagsmithProvider = new FlagsmithProvider(flagsmithClient);


// Assert
Assert.NotNull(flagsmithProvider._flagsmithClient);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<ProjectReference Include="..\..\src\OpenFeature.Contrib.Providers.Flagsmith\OpenFeature.Contrib.Providers.Flagsmith.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Flagsmith" Version="5.1.0" />
</ItemGroup>

<PropertyGroup>
<LangVersion>latest</LangVersion>
</PropertyGroup>
</Project>

0 comments on commit 1a355ab

Please sign in to comment.