From 0f222b4a46d16bd075a9bbc3a512e3d8bf79bee4 Mon Sep 17 00:00:00 2001 From: Eric Holton Date: Fri, 3 Feb 2023 10:01:03 -0700 Subject: [PATCH] fix: GoFeatureFlagUser class was not serialized. (#33) * fix: GoFeatureFlagUser class was not serialized. System.Text.Json does not serialize private members of a class. Since the class `GoFeatureFlagUser` had the following private members: ``` private string Key { get; set; } private bool Anonymous { get; set; } private Dictionary Custom { get; set; } ``` When it was serialized as the User in GOFeatureFlagRequest, the Json that was returned was `{ "user": {}, ...` instead of `{ "user": { "key: : ...` This commit fixes this issue by making the property public and adding a test. * updates documents The name of the package was wrong it should be ***OpenFeature.Contrib.GOFeatureFlag*** and not *OpenFeature.Contrib.Providers.GOFeatureFlag* Signed-off-by: Eric Holton * chore: fix white space Signed-off-by: Eric Holton * chore: fix white space Signed-off-by: Eric Holton --------- Signed-off-by: Eric Holton --- .../GoFeatureFlagProvider.cs | 3 +- .../GoFeatureFlagUser.cs | 115 ++++++++++-------- .../README.md | 16 +-- .../GoFeatureFlagUserTest.cs | 29 +++++ 4 files changed, 102 insertions(+), 61 deletions(-) create mode 100644 test/OpenFeature.Contrib.Providers.GOFeatureFlag.Test/GoFeatureFlagUserTest.cs diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/GoFeatureFlagProvider.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/GoFeatureFlagProvider.cs index ceeb7e96..53eb39af 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/GoFeatureFlagProvider.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/GoFeatureFlagProvider.cs @@ -244,10 +244,9 @@ public override async Task> ResolveStructureValue(strin private async Task CallApi(string flagKey, T defaultValue, EvaluationContext context = null) { - var user = GoFeatureFlagUser.FromEvaluationContext(context); var request = new GOFeatureFlagRequest { - User = user, + User = context, DefaultValue = defaultValue }; var goffRequest = JsonSerializer.Serialize(request, _serializerOptions); diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/GoFeatureFlagUser.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/GoFeatureFlagUser.cs index 25269fee..09742655 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/GoFeatureFlagUser.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/GoFeatureFlagUser.cs @@ -1,52 +1,65 @@ -using System.Collections.Generic; -using System.Linq; -using OpenFeature.Contrib.Providers.GOFeatureFlag.exception; -using OpenFeature.Model; - -namespace OpenFeature.Contrib.Providers.GOFeatureFlag -{ - /// - /// GOFeatureFlagUser is the representation of a User inside GO Feature Flag. - /// - public class GoFeatureFlagUser - { - private const string AnonymousField = "anonymous"; - private const string KeyField = "targetingKey"; - private string Key { get; set; } - private bool Anonymous { get; set; } - private Dictionary Custom { get; set; } - - /** - * FromEvaluationContext convert the evaluation context into a GOFeatureFlagUser Object. - */ - public static GoFeatureFlagUser FromEvaluationContext(EvaluationContext ctx) - { - try - { - if (ctx is null) - throw new InvalidEvaluationContext("GO Feature Flag need an Evaluation context to work."); - if (!ctx.GetValue(KeyField).IsString) - throw new InvalidTargetingKey("targetingKey field MUST be a string."); - } - catch (KeyNotFoundException e) - { - throw new InvalidTargetingKey("targetingKey field is mandatory.", e); - } - - var anonymous = ctx.ContainsKey(AnonymousField) && ctx.GetValue(AnonymousField).IsBoolean - ? ctx.GetValue(AnonymousField).AsBoolean - : false; - - var custom = ctx.AsDictionary().ToDictionary(x => x.Key, x => x.Value.AsObject); - custom.Remove(AnonymousField); - custom.Remove(KeyField); - - return new GoFeatureFlagUser - { - Key = ctx.GetValue("targetingKey").AsString, - Anonymous = anonymous.Value, - Custom = custom - }; - } - } +using System.Collections.Generic; +using System.Linq; + +using OpenFeature.Contrib.Providers.GOFeatureFlag.exception; +using OpenFeature.Model; + +namespace OpenFeature.Contrib.Providers.GOFeatureFlag +{ + /// + /// GOFeatureFlagUser is the representation of a User inside GO Feature Flag. + /// + public class GoFeatureFlagUser + { + private const string AnonymousField = "anonymous"; + private const string KeyField = "targetingKey"; + + /// + /// The targeting key for the user. + /// + public string Key { get; private set; } + + /// + /// Is the user Anonymous. + /// + public bool Anonymous { get; private set; } + + /// + /// Additional Custom Data to pass to GO Feature Flag. + /// + public Dictionary Custom { get; private set; } + + /** + * Convert the evaluation context into a GOFeatureFlagUser Object. + */ + public static implicit operator GoFeatureFlagUser(EvaluationContext ctx) + { + try + { + if (ctx is null) + throw new InvalidEvaluationContext("GO Feature Flag need an Evaluation context to work."); + if (!ctx.GetValue(KeyField).IsString) + throw new InvalidTargetingKey("targetingKey field MUST be a string."); + } + catch (KeyNotFoundException e) + { + throw new InvalidTargetingKey("targetingKey field is mandatory.", e); + } + + var anonymous = ctx.ContainsKey(AnonymousField) && ctx.GetValue(AnonymousField).IsBoolean + ? ctx.GetValue(AnonymousField).AsBoolean + : false; + + var custom = ctx.AsDictionary().ToDictionary(x => x.Key, x => x.Value.AsObject); + custom.Remove(AnonymousField); + custom.Remove(KeyField); + + return new GoFeatureFlagUser + { + Key = ctx.GetValue("targetingKey").AsString, + Anonymous = anonymous.Value, + Custom = custom + }; + } + } } \ No newline at end of file diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/README.md b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/README.md index 38866c22..c56ef6d9 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/README.md +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/README.md @@ -15,32 +15,32 @@ The first things we will do is install the **Open Feature SDK** and the **GO Fea ### .NET Cli ```shell -dotnet add package OpenFeature.Contrib.Providers.GOFeatureFlag +dotnet add package OpenFeature.Contrib.GOFeatureFlag ``` ### Package Manager ```shell -NuGet\Install-Package OpenFeature.Contrib.Providers.GOFeatureFlag +NuGet\Install-Package OpenFeature.Contrib.GOFeatureFlag ``` ### Package Reference ```xml - + ``` ### Packet cli ```shell -paket add OpenFeature.Contrib.Providers.GOFeatureFlag +paket add OpenFeature.Contrib.GOFeatureFlag ``` ### Cake ```shell -// Install OpenFeature.Contrib.Providers.GOFeatureFlag as a Cake Addin -#addin nuget:?package=OpenFeature.Contrib.Providers.GOFeatureFlag +// Install OpenFeature.Contrib.GOFeatureFlag as a Cake Addin +#addin nuget:?package=OpenFeature.Contrib.GOFeatureFlag -// Install OpenFeature.Contrib.Providers.GOFeatureFlag as a Cake Tool -#tool nuget:?package=OpenFeature.Contrib.Providers.GOFeatureFlag +// Install OpenFeature.Contrib.GOFeatureFlag as a Cake Tool +#tool nuget:?package=OpenFeature.Contrib.GOFeatureFlag ``` ## Initialize your Open Feature client diff --git a/test/OpenFeature.Contrib.Providers.GOFeatureFlag.Test/GoFeatureFlagUserTest.cs b/test/OpenFeature.Contrib.Providers.GOFeatureFlag.Test/GoFeatureFlagUserTest.cs new file mode 100644 index 00000000..fba95568 --- /dev/null +++ b/test/OpenFeature.Contrib.Providers.GOFeatureFlag.Test/GoFeatureFlagUserTest.cs @@ -0,0 +1,29 @@ +using System.Text.Json; + +using OpenFeature.Model; + +using Xunit; + +namespace OpenFeature.Contrib.Providers.GOFeatureFlag.Test; + +public class GoFeatureFlagUserTest +{ + [Fact] + public void GoFeatureFlagUserSerializesCorrectly() + { + var userContext = EvaluationContext.Builder() + .Set("targetingKey", "1d1b9238-2591-4a47-94cf-d2bc080892f1") + .Set("firstname", "john") + .Set("lastname", "doe") + .Set("email", "john.doe@gofeatureflag.org") + .Set("admin", true) + .Set("anonymous", false) + .Build(); + + GoFeatureFlagUser user = userContext; + + var userAsString = JsonSerializer.Serialize(user, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); + + Assert.Contains("{\"key\":\"1d1b9238-2591-4a47-94cf-d2bc080892f1\",\"anonymous\":false,\"custom\":{", userAsString); + } +} \ No newline at end of file