From 642c00c78384c9c7270d96abe307ba26a1455e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Fri, 2 May 2025 08:01:08 +0100 Subject: [PATCH 1/3] docs: Update contributing guidelines for .NET version and project setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- CONTRIBUTING.md | 77 ++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 02639db8..c629c9ae 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,23 +2,19 @@ ## System Requirements -Dotnet 6+ is recommended. - -## Compilation target(s) - -As in the Dotnet-SDK, we target C# LangVersion 7.3. The `Common.props` configures this automatically. +Dotnet 8+ is recommended. ## Adding a project -1. Create a new library project under `src/`: `dotnet new classlib -o src/OpenFeature.Contrib.MyComponent --langVersion 7.3` +1. Create a new library project under `src/`: `dotnet new classlib -o src/OpenFeature.Contrib.MyComponent` 2. Create a new test project under `test/`: `dotnet new xunit -o test/OpenFeature.Contrib.MyComponent.Test` 3. Add the library project to the solution: `dotnet sln DotnetSdkContrib.sln add src/OpenFeature.Contrib.MyComponent/OpenFeature.Contrib.MyComponent.csproj` 4. Add the test project to the solution: `dotnet sln DotnetSdkContrib.sln add test/OpenFeature.Contrib.MyComponent.Test/OpenFeature.Contrib.MyComponent.Test.csproj` 5. Add the desired properties to your library's `.csproj` file (see example below). -5. Remove all content besides the root element from your test project's `.csproj` file (all settings will be inherited). -6. Add the new library project to `release-please-config.json`. -7. Add a `version.txt` file to the root of your library with a version matching that in your new `.csproj` file, e.g. `0.0.1`. -8. If you care to release a pre `1.0.0` version, add the same version above to `.release-please-manifest.json`. Failing to do this will release a `1.0.0` initial release. +6. Remove all content besides the root element from your test project's `.csproj` file (all settings will be inherited). +7. Add the new library project to `release-please-config.json`. +8. Add a `version.txt` file to the root of your library with a version matching that in your new `.csproj` file, e.g. `0.0.1`. +9. If you care to release a pre `1.0.0` version, add the same version above to `.release-please-manifest.json`. Failing to do this will release a `1.0.0` initial release. Sample `.csproj` file: @@ -62,34 +58,37 @@ Dependencies used only for building and testing should have a `al ## Consuming pre-release packages 1. Acquire a [GitHub personal access token (PAT)](https://docs.github.com/github/authenticating-to-github/creating-a-personal-access-token) scoped for `read:packages` and verify the permissions: - ```console - $ gh auth login --scopes read:packages - - ? What account do you want to log into? GitHub.com - ? What is your preferred protocol for Git operations? HTTPS - ? How would you like to authenticate GitHub CLI? Login with a web browser - - ! First copy your one-time code: ****-**** - Press Enter to open github.com in your browser... - - ✓ Authentication complete. - - gh config set -h github.com git_protocol https - ✓ Configured git protocol - ✓ Logged in as ******** - ``` - - ```console - $ gh auth status - - github.com - ✓ Logged in to github.com as ******** (~/.config/gh/hosts.yml) - ✓ Git operations for github.com configured to use https protocol. - ✓ Token: gho_************************************ - ✓ Token scopes: gist, read:org, read:packages, repo, workflow - ``` + + ```console + $ gh auth login --scopes read:packages + + ? What account do you want to log into? GitHub.com + ? What is your preferred protocol for Git operations? HTTPS + ? How would you like to authenticate GitHub CLI? Login with a web browser + + ! First copy your one-time code: ****-**** + Press Enter to open github.com in your browser... + + ✓ Authentication complete. + - gh config set -h github.com git_protocol https + ✓ Configured git protocol + ✓ Logged in as ******** + ``` + + ```console + $ gh auth status + + github.com + ✓ Logged in to github.com as ******** (~/.config/gh/hosts.yml) + ✓ Git operations for github.com configured to use https protocol. + ✓ Token: gho_************************************ + ✓ Token scopes: gist, read:org, read:packages, repo, workflow + ``` + 2. Run the following command to configure your local environment to consume packages from GitHub Packages: - ```console - $ dotnet nuget update source github-open-feature --username $(gh api user --jq .email) --password $(gh auth token) --store-password-in-clear-text - Package source "github-open-feature" was successfully updated. - ``` + ```console + $ dotnet nuget update source github-open-feature --username $(gh api user --jq .email) --password $(gh auth token) --store-password-in-clear-text + + Package source "github-open-feature" was successfully updated. + ``` From da6e1b979530688bd770c7d5126037ef71a98cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Fri, 2 May 2025 08:02:10 +0100 Subject: [PATCH 2/3] chore: remove LangVersion property from project files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- .../OpenFeature.Contrib.Providers.Flagsmith.csproj | 4 ---- .../OpenFeature.Contrib.Providers.Flipt.csproj | 4 ---- .../OpenFeature.Contrib.Providers.GOFeatureFlag.csproj | 4 ---- .../OpenFeature.Contrib.Providers.Flagsmith.Test.csproj | 3 --- .../OpenFeature.Contrib.Providers.Flipt.Test.csproj | 1 - .../OpenFeature.Contrib.Providers.GOFeatureFlag.Test.csproj | 3 --- .../OpenFeature.Contrib.Providers.Statsig.Test.csproj | 3 --- 7 files changed, 22 deletions(-) diff --git a/src/OpenFeature.Contrib.Providers.Flagsmith/OpenFeature.Contrib.Providers.Flagsmith.csproj b/src/OpenFeature.Contrib.Providers.Flagsmith/OpenFeature.Contrib.Providers.Flagsmith.csproj index 8e3e5fd3..5cf1f9d2 100644 --- a/src/OpenFeature.Contrib.Providers.Flagsmith/OpenFeature.Contrib.Providers.Flagsmith.csproj +++ b/src/OpenFeature.Contrib.Providers.Flagsmith/OpenFeature.Contrib.Providers.Flagsmith.csproj @@ -24,8 +24,4 @@ - - - latest - diff --git a/src/OpenFeature.Contrib.Providers.Flipt/OpenFeature.Contrib.Providers.Flipt.csproj b/src/OpenFeature.Contrib.Providers.Flipt/OpenFeature.Contrib.Providers.Flipt.csproj index 18ece30e..cc1fb3b5 100644 --- a/src/OpenFeature.Contrib.Providers.Flipt/OpenFeature.Contrib.Providers.Flipt.csproj +++ b/src/OpenFeature.Contrib.Providers.Flipt/OpenFeature.Contrib.Providers.Flipt.csproj @@ -33,10 +33,6 @@ - - latest - - diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/OpenFeature.Contrib.Providers.GOFeatureFlag.csproj b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/OpenFeature.Contrib.Providers.GOFeatureFlag.csproj index 3dc488c8..e37f8469 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/OpenFeature.Contrib.Providers.GOFeatureFlag.csproj +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/OpenFeature.Contrib.Providers.GOFeatureFlag.csproj @@ -15,8 +15,4 @@ - - 8.0 - - diff --git a/test/OpenFeature.Contrib.Providers.Flagsmith.Test/OpenFeature.Contrib.Providers.Flagsmith.Test.csproj b/test/OpenFeature.Contrib.Providers.Flagsmith.Test/OpenFeature.Contrib.Providers.Flagsmith.Test.csproj index 0fd46d64..d362a0dd 100644 --- a/test/OpenFeature.Contrib.Providers.Flagsmith.Test/OpenFeature.Contrib.Providers.Flagsmith.Test.csproj +++ b/test/OpenFeature.Contrib.Providers.Flagsmith.Test/OpenFeature.Contrib.Providers.Flagsmith.Test.csproj @@ -4,7 +4,4 @@ - - latest - \ No newline at end of file diff --git a/test/OpenFeature.Contrib.Providers.Flipt.Test/OpenFeature.Contrib.Providers.Flipt.Test.csproj b/test/OpenFeature.Contrib.Providers.Flipt.Test/OpenFeature.Contrib.Providers.Flipt.Test.csproj index 2ab09f7a..eff8c00b 100644 --- a/test/OpenFeature.Contrib.Providers.Flipt.Test/OpenFeature.Contrib.Providers.Flipt.Test.csproj +++ b/test/OpenFeature.Contrib.Providers.Flipt.Test/OpenFeature.Contrib.Providers.Flipt.Test.csproj @@ -6,7 +6,6 @@ false true - latest diff --git a/test/OpenFeature.Contrib.Providers.GOFeatureFlag.Test/OpenFeature.Contrib.Providers.GOFeatureFlag.Test.csproj b/test/OpenFeature.Contrib.Providers.GOFeatureFlag.Test/OpenFeature.Contrib.Providers.GOFeatureFlag.Test.csproj index c29bafd5..c8dbe5e7 100644 --- a/test/OpenFeature.Contrib.Providers.GOFeatureFlag.Test/OpenFeature.Contrib.Providers.GOFeatureFlag.Test.csproj +++ b/test/OpenFeature.Contrib.Providers.GOFeatureFlag.Test/OpenFeature.Contrib.Providers.GOFeatureFlag.Test.csproj @@ -1,7 +1,4 @@ - - latest - diff --git a/test/OpenFeature.Contrib.Providers.Statsig.Test/OpenFeature.Contrib.Providers.Statsig.Test.csproj b/test/OpenFeature.Contrib.Providers.Statsig.Test/OpenFeature.Contrib.Providers.Statsig.Test.csproj index 88a448cf..a3d57dfc 100644 --- a/test/OpenFeature.Contrib.Providers.Statsig.Test/OpenFeature.Contrib.Providers.Statsig.Test.csproj +++ b/test/OpenFeature.Contrib.Providers.Statsig.Test/OpenFeature.Contrib.Providers.Statsig.Test.csproj @@ -1,7 +1,4 @@ - - latest - From 702128e4588ac2ac1acaad862199754dbcc23b52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Fri, 2 May 2025 08:03:06 +0100 Subject: [PATCH 3/3] Refactor converters and exception handling in GOFeatureFlag provider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Updated DictionaryConverter to improve code clarity and structure. - Enhanced JsonConverterExtensions with better documentation. - Refined OpenFeatureStructureConverter and OpenFeatureValueConverter for consistency and readability. - Simplified exception classes (FlagDisabled, FlagNotFoundError, GeneralError, etc.) by removing unnecessary comments and improving documentation. - Improved error handling in GoFeatureFlagException and its derived classes. - Streamlined the implementation of GoFeatureFlagExtensions for better usability. - Enhanced EnrichEvaluationContextHook to clarify its purpose and improve metadata handling. - Refactored ExporterMetadata to encapsulate metadata building logic more effectively. - Improved the structure and clarity of OfrepRequest and OfrepResponse classes. Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- .../GoFeatureFlagProvider.cs | 579 +++++++++--------- .../GoFeatureFlagProviderOptions.cs | 65 +- .../converters/DictionaryConverter.cs | 101 ++- .../converters/JsonConverterExtensions.cs | 31 +- .../OpenFeatureStructureConverter.cs | 39 +- .../converters/OpenFeatureValueConverter.cs | 147 +++-- .../exception/FlagDisabled.cs | 13 +- .../exception/FlagNotFoundError.cs | 23 +- .../exception/GeneralError.cs | 23 +- .../exception/GoFeatureFlagException.cs | 51 +- .../exception/ImpossibleToConvertTypeError.cs | 25 +- .../exception/InvalidEvaluationContext.cs | 23 +- .../exception/InvalidOption.cs | 19 +- .../exception/InvalidTargetingKey.cs | 23 +- .../exception/TypeMismatchError.cs | 23 +- .../exception/UnauthorizedError.cs | 23 +- .../extensions/GoFeatureFlagExtensions.cs | 25 +- .../hooks/EnrichEvaluationContextHook.cs | 57 +- .../models/ExporterMetadata.cs | 97 ++- .../models/OfrepRequest.cs | 67 +- .../models/OfrepResponse.cs | 89 ++- 21 files changed, 761 insertions(+), 782 deletions(-) diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/GoFeatureFlagProvider.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/GoFeatureFlagProvider.cs index 4b22ec8a..2d0c364e 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/GoFeatureFlagProvider.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/GoFeatureFlagProvider.cs @@ -17,347 +17,346 @@ using OpenFeature.Contrib.Providers.GOFeatureFlag.models; using OpenFeature.Model; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag +namespace OpenFeature.Contrib.Providers.GOFeatureFlag; + +/// +/// GoFeatureFlagProvider is the OpenFeature provider for GO Feature Flag. +/// +public class GoFeatureFlagProvider : FeatureProvider { + private const string ApplicationJson = "application/json"; + private ExporterMetadata _exporterMetadata; + private HttpClient _httpClient; + + /// + /// Constructor of the provider. + /// Options used while creating the provider + /// if no options are provided or we have a wrong configuration. + /// + public GoFeatureFlagProvider(GoFeatureFlagProviderOptions options) + { + ValidateInputOptions(options); + InitializeProvider(options); + } + + /// + /// List of hooks to use for this provider + /// + /// + public override IImmutableList GetProviderHooks() + { + var hooks = ImmutableArray.CreateBuilder(); + hooks.Add(new EnrichEvaluationContextHook(_exporterMetadata)); + return hooks.ToImmutable(); + } + + /// + /// validateInputOptions is validating the different options provided when creating the provider. + /// + /// Options used while creating the provider + /// if no options are provided or we have a wrong configuration. + private void ValidateInputOptions(GoFeatureFlagProviderOptions options) + { + if (options is null) throw new InvalidOption("No options provided"); + + if (string.IsNullOrEmpty(options.Endpoint)) + throw new InvalidOption("endpoint is a mandatory field when initializing the provider"); + } + /// - /// GoFeatureFlagProvider is the OpenFeature provider for GO Feature Flag. + /// initializeProvider is initializing the different class element used by the provider. /// - public class GoFeatureFlagProvider : FeatureProvider + /// Options used while creating the provider + private void InitializeProvider(GoFeatureFlagProviderOptions options) { - private const string ApplicationJson = "application/json"; - private ExporterMetadata _exporterMetadata; - private HttpClient _httpClient; - - /// - /// Constructor of the provider. - /// Options used while creating the provider - /// if no options are provided or we have a wrong configuration. - /// - public GoFeatureFlagProvider(GoFeatureFlagProviderOptions options) + _exporterMetadata = options.ExporterMetadata ?? new ExporterMetadata(); + _exporterMetadata.Add("provider", ".NET"); + _exporterMetadata.Add("openfeature", true); + + _httpClient = options.HttpMessageHandler != null + ? new HttpClient(options.HttpMessageHandler) + : new HttpClient + { + Timeout = options.Timeout.Ticks.Equals(0) + ? new TimeSpan(10000 * TimeSpan.TicksPerMillisecond) + : options.Timeout + }; + _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(ApplicationJson)); + _httpClient.BaseAddress = new Uri(options.Endpoint); + + if (options.ApiKey != null) + _httpClient.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", options.ApiKey); + } + + /// + /// Return the metadata associated to this provider. + /// + public override Metadata GetMetadata() + { + return new Metadata("GO Feature Flag Provider"); + } + + /// + /// ResolveBooleanValueAsync resolve the value for a Boolean Flag. + /// + /// Name of the flag + /// Default value used in case of error. + /// Context about the user + /// Token for cancel the async operation + /// A ResolutionDetails object containing the value of your flag + /// If the type of the flag does not match + /// If the flag does not exists + /// If an unknown error happen + /// If the flag is disabled + public override async Task> ResolveBooleanValueAsync(string flagKey, bool defaultValue, + EvaluationContext context = null, CancellationToken cancellationToken = default) + { + try { - ValidateInputOptions(options); - InitializeProvider(options); + var resp = await CallApi(flagKey, defaultValue, context).ConfigureAwait(false); + return new ResolutionDetails(flagKey, bool.Parse(resp.Value.ToString()), ErrorType.None, + resp.Reason, resp.Variant, resp.ErrorDetails, resp.Metadata.ToImmutableMetadata()); } - - /// - /// List of hooks to use for this provider - /// - /// - public override IImmutableList GetProviderHooks() + catch (FormatException e) { - var hooks = ImmutableArray.CreateBuilder(); - hooks.Add(new EnrichEvaluationContextHook(_exporterMetadata)); - return hooks.ToImmutable(); + throw new TypeMismatchError($"flag value {flagKey} had unexpected type", e); } - - /// - /// validateInputOptions is validating the different options provided when creating the provider. - /// - /// Options used while creating the provider - /// if no options are provided or we have a wrong configuration. - private void ValidateInputOptions(GoFeatureFlagProviderOptions options) + catch (FlagDisabled) { - if (options is null) throw new InvalidOption("No options provided"); - - if (string.IsNullOrEmpty(options.Endpoint)) - throw new InvalidOption("endpoint is a mandatory field when initializing the provider"); + return new ResolutionDetails(flagKey, defaultValue, ErrorType.None, Reason.Disabled); } + } - /// - /// initializeProvider is initializing the different class element used by the provider. - /// - /// Options used while creating the provider - private void InitializeProvider(GoFeatureFlagProviderOptions options) + /// + /// ResolveBooleanValueAsync resolve the value for a string Flag. + /// + /// Name of the flag + /// Default value used in case of error. + /// Context about the user + /// Token for cancel the async operation + /// A ResolutionDetails object containing the value of your flag + /// If the type of the flag does not match + /// If the flag does not exists + /// If an unknown error happen + /// If the flag is disabled + public override async Task> ResolveStringValueAsync(string flagKey, + string defaultValue, + EvaluationContext context = null, CancellationToken cancellationToken = default) + { + try { - _exporterMetadata = options.ExporterMetadata ?? new ExporterMetadata(); - _exporterMetadata.Add("provider", ".NET"); - _exporterMetadata.Add("openfeature", true); - - _httpClient = options.HttpMessageHandler != null - ? new HttpClient(options.HttpMessageHandler) - : new HttpClient - { - Timeout = options.Timeout.Ticks.Equals(0) - ? new TimeSpan(10000 * TimeSpan.TicksPerMillisecond) - : options.Timeout - }; - _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(ApplicationJson)); - _httpClient.BaseAddress = new Uri(options.Endpoint); - - if (options.ApiKey != null) - _httpClient.DefaultRequestHeaders.Authorization = - new AuthenticationHeaderValue("Bearer", options.ApiKey); + var resp = await CallApi(flagKey, defaultValue, context).ConfigureAwait(false); + if (!(resp.Value is JsonElement element && element.ValueKind == JsonValueKind.String)) + throw new TypeMismatchError($"flag value {flagKey} had unexpected type"); + return new ResolutionDetails(flagKey, resp.Value.ToString(), ErrorType.None, resp.Reason, + resp.Variant, resp.ErrorDetails, resp.Metadata.ToImmutableMetadata()); } - - /// - /// Return the metadata associated to this provider. - /// - public override Metadata GetMetadata() + catch (FormatException e) { - return new Metadata("GO Feature Flag Provider"); + throw new TypeMismatchError($"flag value {flagKey} had unexpected type", e); } - - /// - /// ResolveBooleanValueAsync resolve the value for a Boolean Flag. - /// - /// Name of the flag - /// Default value used in case of error. - /// Context about the user - /// Token for cancel the async operation - /// A ResolutionDetails object containing the value of your flag - /// If the type of the flag does not match - /// If the flag does not exists - /// If an unknown error happen - /// If the flag is disabled - public override async Task> ResolveBooleanValueAsync(string flagKey, bool defaultValue, - EvaluationContext context = null, CancellationToken cancellationToken = default) + catch (FlagDisabled) { - try - { - var resp = await CallApi(flagKey, defaultValue, context).ConfigureAwait(false); - return new ResolutionDetails(flagKey, bool.Parse(resp.Value.ToString()), ErrorType.None, - resp.Reason, resp.Variant, resp.ErrorDetails, resp.Metadata.ToImmutableMetadata()); - } - catch (FormatException e) - { - throw new TypeMismatchError($"flag value {flagKey} had unexpected type", e); - } - catch (FlagDisabled) - { - return new ResolutionDetails(flagKey, defaultValue, ErrorType.None, Reason.Disabled); - } + return new ResolutionDetails(flagKey, defaultValue, ErrorType.None, Reason.Disabled); } + } - /// - /// ResolveBooleanValueAsync resolve the value for a string Flag. - /// - /// Name of the flag - /// Default value used in case of error. - /// Context about the user - /// Token for cancel the async operation - /// A ResolutionDetails object containing the value of your flag - /// If the type of the flag does not match - /// If the flag does not exists - /// If an unknown error happen - /// If the flag is disabled - public override async Task> ResolveStringValueAsync(string flagKey, - string defaultValue, - EvaluationContext context = null, CancellationToken cancellationToken = default) + /// + /// ResolveBooleanValueAsync resolve the value for an int Flag. + /// + /// Name of the flag + /// Default value used in case of error. + /// Context about the user + /// Token for cancel the async operation + /// A ResolutionDetails object containing the value of your flag + /// If the type of the flag does not match + /// If the flag does not exists + /// If an unknown error happen + /// If the flag is disabled + public override async Task> ResolveIntegerValueAsync(string flagKey, int defaultValue, + EvaluationContext context = null, CancellationToken cancellationToken = default) + { + try { - try - { - var resp = await CallApi(flagKey, defaultValue, context).ConfigureAwait(false); - if (!(resp.Value is JsonElement element && element.ValueKind == JsonValueKind.String)) - throw new TypeMismatchError($"flag value {flagKey} had unexpected type"); - return new ResolutionDetails(flagKey, resp.Value.ToString(), ErrorType.None, resp.Reason, - resp.Variant, resp.ErrorDetails, resp.Metadata.ToImmutableMetadata()); - } - catch (FormatException e) - { - throw new TypeMismatchError($"flag value {flagKey} had unexpected type", e); - } - catch (FlagDisabled) - { - return new ResolutionDetails(flagKey, defaultValue, ErrorType.None, Reason.Disabled); - } + var resp = await CallApi(flagKey, defaultValue, context).ConfigureAwait(false); + return new ResolutionDetails(flagKey, int.Parse(resp.Value.ToString()), ErrorType.None, + resp.Reason, resp.Variant, resp.ErrorDetails, resp.Metadata.ToImmutableMetadata()); } - - /// - /// ResolveBooleanValueAsync resolve the value for an int Flag. - /// - /// Name of the flag - /// Default value used in case of error. - /// Context about the user - /// Token for cancel the async operation - /// A ResolutionDetails object containing the value of your flag - /// If the type of the flag does not match - /// If the flag does not exists - /// If an unknown error happen - /// If the flag is disabled - public override async Task> ResolveIntegerValueAsync(string flagKey, int defaultValue, - EvaluationContext context = null, CancellationToken cancellationToken = default) + catch (FormatException e) { - try - { - var resp = await CallApi(flagKey, defaultValue, context).ConfigureAwait(false); - return new ResolutionDetails(flagKey, int.Parse(resp.Value.ToString()), ErrorType.None, - resp.Reason, resp.Variant, resp.ErrorDetails, resp.Metadata.ToImmutableMetadata()); - } - catch (FormatException e) - { - throw new TypeMismatchError($"flag value {flagKey} had unexpected type", e); - } - catch (FlagDisabled) - { - return new ResolutionDetails(flagKey, defaultValue, ErrorType.None, Reason.Disabled); - } + throw new TypeMismatchError($"flag value {flagKey} had unexpected type", e); } - - /// - /// ResolveBooleanValueAsync resolve the value for a double Flag. - /// - /// Name of the flag - /// Default value used in case of error. - /// Context about the user - /// Token for cancel the async operation - /// A ResolutionDetails object containing the value of your flag - /// If the type of the flag does not match - /// If the flag does not exists - /// If an unknown error happen - /// If the flag is disabled - public override async Task> ResolveDoubleValueAsync(string flagKey, - double defaultValue, - EvaluationContext context = null, CancellationToken cancellationToken = default) + catch (FlagDisabled) { - try - { - var resp = await CallApi(flagKey, defaultValue, context).ConfigureAwait(false); - return new ResolutionDetails(flagKey, - double.Parse(resp.Value.ToString(), CultureInfo.InvariantCulture), ErrorType.None, - resp.Reason, resp.Variant, resp.ErrorDetails, resp.Metadata.ToImmutableMetadata()); - } - catch (FormatException e) - { - throw new TypeMismatchError($"flag value {flagKey} had unexpected type", e); - } - catch (FlagDisabled) - { - return new ResolutionDetails(flagKey, defaultValue, ErrorType.None, Reason.Disabled); - } + return new ResolutionDetails(flagKey, defaultValue, ErrorType.None, Reason.Disabled); } + } - /// - /// ResolveBooleanValueAsync resolve the value for a Boolean Flag. - /// - /// Name of the flag - /// Default value used in case of error. - /// Context about the user - /// Token for cancel the async operation - /// A ResolutionDetails object containing the value of your flag - /// If the type of the flag does not match - /// If the flag does not exists - /// If an unknown error happen - /// If the flag is disabled - public override async Task> ResolveStructureValueAsync(string flagKey, - Value defaultValue, - EvaluationContext context = null, CancellationToken cancellationToken = default) + /// + /// ResolveBooleanValueAsync resolve the value for a double Flag. + /// + /// Name of the flag + /// Default value used in case of error. + /// Context about the user + /// Token for cancel the async operation + /// A ResolutionDetails object containing the value of your flag + /// If the type of the flag does not match + /// If the flag does not exists + /// If an unknown error happen + /// If the flag is disabled + public override async Task> ResolveDoubleValueAsync(string flagKey, + double defaultValue, + EvaluationContext context = null, CancellationToken cancellationToken = default) + { + try { - try - { - var resp = await CallApi(flagKey, defaultValue, context).ConfigureAwait(false); - if (resp.Value is JsonElement) - { - var value = ConvertValue((JsonElement)resp.Value); - return new ResolutionDetails(flagKey, value, ErrorType.None, resp.Reason, - resp.Variant, resp.ErrorDetails, resp.Metadata.ToImmutableMetadata()); - } + var resp = await CallApi(flagKey, defaultValue, context).ConfigureAwait(false); + return new ResolutionDetails(flagKey, + double.Parse(resp.Value.ToString(), CultureInfo.InvariantCulture), ErrorType.None, + resp.Reason, resp.Variant, resp.ErrorDetails, resp.Metadata.ToImmutableMetadata()); + } + catch (FormatException e) + { + throw new TypeMismatchError($"flag value {flagKey} had unexpected type", e); + } + catch (FlagDisabled) + { + return new ResolutionDetails(flagKey, defaultValue, ErrorType.None, Reason.Disabled); + } + } - throw new TypeMismatchError($"flag value {flagKey} had unexpected type"); - } - catch (FormatException e) - { - throw new TypeMismatchError($"flag value {flagKey} had unexpected type", e); - } - catch (FlagDisabled) + /// + /// ResolveBooleanValueAsync resolve the value for a Boolean Flag. + /// + /// Name of the flag + /// Default value used in case of error. + /// Context about the user + /// Token for cancel the async operation + /// A ResolutionDetails object containing the value of your flag + /// If the type of the flag does not match + /// If the flag does not exists + /// If an unknown error happen + /// If the flag is disabled + public override async Task> ResolveStructureValueAsync(string flagKey, + Value defaultValue, + EvaluationContext context = null, CancellationToken cancellationToken = default) + { + try + { + var resp = await CallApi(flagKey, defaultValue, context).ConfigureAwait(false); + if (resp.Value is JsonElement) { - return new ResolutionDetails(flagKey, defaultValue, ErrorType.None, Reason.Disabled); + var value = ConvertValue((JsonElement)resp.Value); + return new ResolutionDetails(flagKey, value, ErrorType.None, resp.Reason, + resp.Variant, resp.ErrorDetails, resp.Metadata.ToImmutableMetadata()); } - } - /// - /// This method is handling the call to the GO Feature Flag Relay proxy. - /// - /// Name of the flag - /// Default value - /// EvaluationContext to convert as parameters for GO Feature Flag Relay Proxy - /// Type of the data we should retrieve - /// The API response in a GoFeatureFlagResponse object. - /// If the flag does not exists - /// If an unknown error happen - /// If the flag is disabled - private async Task CallApi(string flagKey, T defaultValue, - EvaluationContext context = null) + throw new TypeMismatchError($"flag value {flagKey} had unexpected type"); + } + catch (FormatException e) + { + throw new TypeMismatchError($"flag value {flagKey} had unexpected type", e); + } + catch (FlagDisabled) { - var request = new OfrepRequest(context); - var response = await _httpClient.PostAsync($"ofrep/v1/evaluate/flags/{flagKey}", - new StringContent(request.AsJsonString(), Encoding.UTF8, ApplicationJson)).ConfigureAwait(false); + return new ResolutionDetails(flagKey, defaultValue, ErrorType.None, Reason.Disabled); + } + } - if (response.StatusCode == HttpStatusCode.NotFound) - throw new FlagNotFoundError($"flag {flagKey} was not found in your configuration"); + /// + /// This method is handling the call to the GO Feature Flag Relay proxy. + /// + /// Name of the flag + /// Default value + /// EvaluationContext to convert as parameters for GO Feature Flag Relay Proxy + /// Type of the data we should retrieve + /// The API response in a GoFeatureFlagResponse object. + /// If the flag does not exists + /// If an unknown error happen + /// If the flag is disabled + private async Task CallApi(string flagKey, T defaultValue, + EvaluationContext context = null) + { + var request = new OfrepRequest(context); + var response = await _httpClient.PostAsync($"ofrep/v1/evaluate/flags/{flagKey}", + new StringContent(request.AsJsonString(), Encoding.UTF8, ApplicationJson)).ConfigureAwait(false); - if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden) - throw new UnauthorizedError("invalid token used to contact GO Feature Flag relay proxy instance"); + if (response.StatusCode == HttpStatusCode.NotFound) + throw new FlagNotFoundError($"flag {flagKey} was not found in your configuration"); - if (response.StatusCode >= HttpStatusCode.BadRequest) - throw new GeneralError("impossible to contact GO Feature Flag relay proxy instance"); + if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden) + throw new UnauthorizedError("invalid token used to contact GO Feature Flag relay proxy instance"); - var responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - var options = new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }; - var ofrepResp = - JsonSerializer.Deserialize(responseBody, options); + if (response.StatusCode >= HttpStatusCode.BadRequest) + throw new GeneralError("impossible to contact GO Feature Flag relay proxy instance"); - if (Reason.Disabled.Equals(ofrepResp?.Reason)) - throw new FlagDisabled(); + var responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + var options = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }; + var ofrepResp = + JsonSerializer.Deserialize(responseBody, options); - if ("FLAG_NOT_FOUND".Equals(ofrepResp?.ErrorCode)) - throw new FlagNotFoundError($"flag {flagKey} was not found in your configuration"); + if (Reason.Disabled.Equals(ofrepResp?.Reason)) + throw new FlagDisabled(); - if (ofrepResp?.Metadata != null) - ofrepResp.Metadata = DictionaryConverter.ConvertDictionary(ofrepResp.Metadata); + if ("FLAG_NOT_FOUND".Equals(ofrepResp?.ErrorCode)) + throw new FlagNotFoundError($"flag {flagKey} was not found in your configuration"); - return ofrepResp; - } + if (ofrepResp?.Metadata != null) + ofrepResp.Metadata = DictionaryConverter.ConvertDictionary(ofrepResp.Metadata); - /// - /// convertValue is converting the object return by the proxy response in the right type. - /// - /// The value we have received - /// A converted object - /// If we are not able to convert the data. - private Value ConvertValue(JsonElement value) - { - if (value.ValueKind == JsonValueKind.Null || value.ValueKind == JsonValueKind.Undefined) return null; + return ofrepResp; + } + + /// + /// convertValue is converting the object return by the proxy response in the right type. + /// + /// The value we have received + /// A converted object + /// If we are not able to convert the data. + private Value ConvertValue(JsonElement value) + { + 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.False || value.ValueKind == JsonValueKind.True) + return new Value(value.GetBoolean()); - if (value.ValueKind == JsonValueKind.Number) return new Value(value.GetDouble()); + if (value.ValueKind == JsonValueKind.Number) return new Value(value.GetDouble()); - if (value.ValueKind == JsonValueKind.Object) + if (value.ValueKind == JsonValueKind.Object) + { + var dict = new Dictionary(); + using var objEnumerator = value.EnumerateObject(); + while (objEnumerator.MoveNext()) { - var dict = new Dictionary(); - using var objEnumerator = value.EnumerateObject(); - while (objEnumerator.MoveNext()) - { - var current = objEnumerator.Current; - var currentValue = ConvertValue(current.Value); - if (currentValue != null) dict.Add(current.Name, ConvertValue(current.Value)); - } - - return new Value(new Structure(dict)); + var current = objEnumerator.Current; + var currentValue = ConvertValue(current.Value); + if (currentValue != null) dict.Add(current.Name, ConvertValue(current.Value)); } - if (value.ValueKind == JsonValueKind.String) return new Value(value.ToString()); + return new Value(new Structure(dict)); + } - if (value.ValueKind == JsonValueKind.Array) - { - using var arrayEnumerator = value.EnumerateArray(); - var arr = new List(); + if (value.ValueKind == JsonValueKind.String) return new Value(value.ToString()); - while (arrayEnumerator.MoveNext()) - { - var current = arrayEnumerator.Current; - var convertedValue = ConvertValue(current); - if (convertedValue != null) arr.Add(convertedValue); - } + if (value.ValueKind == JsonValueKind.Array) + { + using var arrayEnumerator = value.EnumerateArray(); + var arr = new List(); - return new Value(arr); + while (arrayEnumerator.MoveNext()) + { + var current = arrayEnumerator.Current; + var convertedValue = ConvertValue(current); + if (convertedValue != null) arr.Add(convertedValue); } - throw new ImpossibleToConvertTypeError($"impossible to convert the object {value}"); + return new Value(arr); } + + throw new ImpossibleToConvertTypeError($"impossible to convert the object {value}"); } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/GoFeatureFlagProviderOptions.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/GoFeatureFlagProviderOptions.cs index 3d3100cc..2dd08639 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/GoFeatureFlagProviderOptions.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/GoFeatureFlagProviderOptions.cs @@ -2,44 +2,43 @@ using System.Net.Http; using OpenFeature.Contrib.Providers.GOFeatureFlag.models; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag +namespace OpenFeature.Contrib.Providers.GOFeatureFlag; + +/// +/// GoFeatureFlagProviderOptions contains the options to initialise the provider. +/// +public class GoFeatureFlagProviderOptions { /// - /// GoFeatureFlagProviderOptions contains the options to initialise the provider. + /// (mandatory) endpoint contains the DNS of your GO Feature Flag relay proxy + /// example: https://mydomain.com/gofeatureflagproxy/ /// - public class GoFeatureFlagProviderOptions - { - /// - /// (mandatory) endpoint contains the DNS of your GO Feature Flag relay proxy - /// example: https://mydomain.com/gofeatureflagproxy/ - /// - public string Endpoint { get; set; } + public string Endpoint { get; set; } - /// - /// (optional) timeout we are waiting when calling the go-feature-flag relay proxy API. - /// Default: 10000 ms - /// - public TimeSpan Timeout { get; set; } = new TimeSpan(10000 * TimeSpan.TicksPerMillisecond); + /// + /// (optional) timeout we are waiting when calling the go-feature-flag relay proxy API. + /// Default: 10000 ms + /// + public TimeSpan Timeout { get; set; } = new TimeSpan(10000 * TimeSpan.TicksPerMillisecond); - /// - /// (optional) If you want to provide your own HttpMessageHandler. - /// Default: null - /// - public HttpMessageHandler HttpMessageHandler { get; set; } + /// + /// (optional) If you want to provide your own HttpMessageHandler. + /// Default: null + /// + public HttpMessageHandler HttpMessageHandler { get; set; } - /// - /// (optional) If the relay proxy is configured to authenticate the request, you should provide - /// an API Key to the provider. - /// Please ask the administrator of the relay proxy to provide an API Key. - /// (This feature is available only if you are using GO Feature Flag relay proxy v1.7.0 or above) - /// Default: null - /// - public string ApiKey { get; set; } + /// + /// (optional) If the relay proxy is configured to authenticate the request, you should provide + /// an API Key to the provider. + /// Please ask the administrator of the relay proxy to provide an API Key. + /// (This feature is available only if you are using GO Feature Flag relay proxy v1.7.0 or above) + /// Default: null + /// + public string ApiKey { get; set; } - /// - /// (optional) ExporterMetadata are static information you can set that will be available in the - /// evaluation data sent to the exporter. - /// - public ExporterMetadata ExporterMetadata { get; set; } - } + /// + /// (optional) ExporterMetadata are static information you can set that will be available in the + /// evaluation data sent to the exporter. + /// + public ExporterMetadata ExporterMetadata { get; set; } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/converters/DictionaryConverter.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/converters/DictionaryConverter.cs index e7f9f319..524dc52d 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/converters/DictionaryConverter.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/converters/DictionaryConverter.cs @@ -2,64 +2,63 @@ using System.Linq; using System.Text.Json; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag.converters +namespace OpenFeature.Contrib.Providers.GOFeatureFlag.converters; + +/// +/// DictionaryConverter is converting a json Dictionary to a Dictionary with real object. +/// +public static class DictionaryConverter { /// - /// DictionaryConverter is converting a json Dictionary to a Dictionary with real object. + /// Function that convert the dictionary to a Dictionary with real object. /// - public static class DictionaryConverter + /// + /// A dictionary with real types. + public static Dictionary ConvertDictionary(Dictionary inputDictionary) { - /// - /// Function that convert the dictionary to a Dictionary with real object. - /// - /// - /// A dictionary with real types. - public static Dictionary ConvertDictionary(Dictionary inputDictionary) - { - return inputDictionary.ToDictionary( - kvp => kvp.Key, - kvp => ConvertValue(kvp.Value) - ); - } + return inputDictionary.ToDictionary( + kvp => kvp.Key, + kvp => ConvertValue(kvp.Value) + ); + } - /// - /// Function that convert a value to a object. - /// - /// - /// A value with real types. - public static object ConvertValue(object value) - { - if (value is JsonElement jsonElement) - switch (jsonElement.ValueKind) - { - case JsonValueKind.String: - return jsonElement.GetString(); - case JsonValueKind.Number: - if (jsonElement.TryGetInt32(out var intValue)) return intValue; + /// + /// Function that convert a value to a object. + /// + /// + /// A value with real types. + public static object ConvertValue(object value) + { + if (value is JsonElement jsonElement) + switch (jsonElement.ValueKind) + { + case JsonValueKind.String: + return jsonElement.GetString(); + case JsonValueKind.Number: + if (jsonElement.TryGetInt32(out var intValue)) return intValue; - if (jsonElement.TryGetDouble(out var doubleValue)) return doubleValue; - return jsonElement.GetRawText(); // Fallback to string if not int or double - case JsonValueKind.True: - return true; - case JsonValueKind.False: - return false; - case JsonValueKind.Null: - return null; - case JsonValueKind.Object: - return ConvertDictionary( - JsonSerializer - .Deserialize>(jsonElement - .GetRawText())); //Recursive for nested objects - case JsonValueKind.Array: - var array = new List(); - foreach (var element in jsonElement.EnumerateArray()) array.Add(ConvertValue(element)); + if (jsonElement.TryGetDouble(out var doubleValue)) return doubleValue; + return jsonElement.GetRawText(); // Fallback to string if not int or double + case JsonValueKind.True: + return true; + case JsonValueKind.False: + return false; + case JsonValueKind.Null: + return null; + case JsonValueKind.Object: + return ConvertDictionary( + JsonSerializer + .Deserialize>(jsonElement + .GetRawText())); //Recursive for nested objects + case JsonValueKind.Array: + var array = new List(); + foreach (var element in jsonElement.EnumerateArray()) array.Add(ConvertValue(element)); - return array; - default: - return jsonElement.GetRawText(); // Handle other types as needed - } + return array; + default: + return jsonElement.GetRawText(); // Handle other types as needed + } - return value; // Return original value if not a JsonElement - } + return value; // Return original value if not a JsonElement } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/converters/JsonConverterExtensions.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/converters/JsonConverterExtensions.cs index 2032d748..faee45a7 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/converters/JsonConverterExtensions.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/converters/JsonConverterExtensions.cs @@ -1,24 +1,23 @@ using System.Text.Json; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag.converters +namespace OpenFeature.Contrib.Providers.GOFeatureFlag.converters; + +/// +/// Extensions for default JsonConverter behavior +/// +public static class JsonConverterExtensions { /// - /// Extensions for default JsonConverter behavior + /// JsonConverter serializer settings for GO Feature Flag to OpenFeature model deserialization /// - public static class JsonConverterExtensions + public static readonly JsonSerializerOptions DefaultSerializerSettings = new JsonSerializerOptions { - /// - /// JsonConverter serializer settings for GO Feature Flag to OpenFeature model deserialization - /// - public static readonly JsonSerializerOptions DefaultSerializerSettings = new JsonSerializerOptions + WriteIndented = true, + AllowTrailingCommas = true, + Converters = { - WriteIndented = true, - AllowTrailingCommas = true, - Converters = - { - new OpenFeatureStructureConverter(), - new OpenFeatureValueConverter() - } - }; - } + new OpenFeatureStructureConverter(), + new OpenFeatureValueConverter() + } + }; } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/converters/OpenFeatureStructureConverter.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/converters/OpenFeatureStructureConverter.cs index fedfef08..43f9705b 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/converters/OpenFeatureStructureConverter.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/converters/OpenFeatureStructureConverter.cs @@ -4,28 +4,27 @@ using System.Text.Json.Serialization; using OpenFeature.Model; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag.converters +namespace OpenFeature.Contrib.Providers.GOFeatureFlag.converters; + +/// +/// OpenFeatureStructureConverter +/// +public class OpenFeatureStructureConverter : JsonConverter { - /// - /// OpenFeatureStructureConverter - /// - public class OpenFeatureStructureConverter : JsonConverter + /// + public override void Write(Utf8JsonWriter writer, Structure value, JsonSerializerOptions options) { - /// - public override void Write(Utf8JsonWriter writer, Structure value, JsonSerializerOptions options) - { - var jsonDoc = JsonDocument.Parse(JsonSerializer.Serialize(value.AsDictionary(), - JsonConverterExtensions.DefaultSerializerSettings)); - jsonDoc.WriteTo(writer); - } + var jsonDoc = JsonDocument.Parse(JsonSerializer.Serialize(value.AsDictionary(), + JsonConverterExtensions.DefaultSerializerSettings)); + jsonDoc.WriteTo(writer); + } - /// - 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>(jsonText, - JsonConverterExtensions.DefaultSerializerSettings)); - } + /// + 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>(jsonText, + JsonConverterExtensions.DefaultSerializerSettings)); } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/converters/OpenFeatureValueConverter.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/converters/OpenFeatureValueConverter.cs index 1363b173..b88a46ae 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/converters/OpenFeatureValueConverter.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/converters/OpenFeatureValueConverter.cs @@ -4,100 +4,99 @@ using System.Text.Json.Serialization; using OpenFeature.Model; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag.converters +namespace OpenFeature.Contrib.Providers.GOFeatureFlag.converters; + +/// +/// OpenFeature Value type converter +/// +public class OpenFeatureValueConverter : JsonConverter { - /// - /// OpenFeature Value type converter - /// - public class OpenFeatureValueConverter : JsonConverter + /// + public override Value Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - /// - public override Value Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + var value = new Value(); + switch (reader.TokenType) { - 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; + 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)); } - private Structure GetStructure(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + return value; + } + + private Structure GetStructure(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var startDepth = reader.CurrentDepth; + var structureDictionary = new Dictionary(); + while (reader.Read()) { - var startDepth = reader.CurrentDepth; - var structureDictionary = new Dictionary(); - while (reader.Read()) + if (reader.TokenType == JsonTokenType.PropertyName) { - 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; + var key = reader.GetString(); + reader.Read(); + var val = Read(ref reader, typeToConvert, options); + structureDictionary[key ?? string.Empty] = val; } - return new Structure(structureDictionary); + if (reader.TokenType == JsonTokenType.EndObject && reader.CurrentDepth == startDepth) break; } + return new Structure(structureDictionary); + } - private IList GenerateValueArray(ref Utf8JsonReader reader, Type typeToConvert, - JsonSerializerOptions options) - { - var valuesArray = new List(); - 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; - } + private IList GenerateValueArray(ref Utf8JsonReader reader, Type typeToConvert, + JsonSerializerOptions options) + { + var valuesArray = new List(); + var startDepth = reader.CurrentDepth; - /// - public override void Write(Utf8JsonWriter writer, Value value, JsonSerializerOptions options) - { - if (value.IsList) + while (reader.Read()) + switch (reader.TokenType) { - writer.WriteStartArray(); - foreach (var val in value.AsList!) - { - var jsonDoc = JsonDocument.Parse(JsonSerializer.Serialize(val.AsObject, - JsonConverterExtensions.DefaultSerializerSettings)); - jsonDoc.WriteTo(writer); - } - - writer.WriteEndArray(); + case JsonTokenType.EndArray when reader.CurrentDepth == startDepth: + return valuesArray; + default: + valuesArray.Add(Read(ref reader, typeToConvert, options)); + break; } - else + + return valuesArray; + } + + /// + 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(value.AsObject, + 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); } } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/FlagDisabled.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/FlagDisabled.cs index bd257978..d5207e70 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/FlagDisabled.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/FlagDisabled.cs @@ -1,9 +1,8 @@ -namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception +namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception; + +/// +/// Exception thrown when a flag is disabled +/// +public class FlagDisabled : GoFeatureFlagException { - /// - /// Exception thrown when a flag is disabled - /// - public class FlagDisabled : GoFeatureFlagException - { - } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/FlagNotFoundError.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/FlagNotFoundError.cs index 4f0e0ca8..ab41b675 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/FlagNotFoundError.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/FlagNotFoundError.cs @@ -2,21 +2,20 @@ using OpenFeature.Constant; using OpenFeature.Error; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception +namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception; + +/// +/// Exception thrown when the flag is not found by GO Feature Flag relay proxy. +/// +public class FlagNotFoundError : FeatureProviderException { /// - /// Exception thrown when the flag is not found by GO Feature Flag relay proxy. + /// Constructor of the exception /// - public class FlagNotFoundError : FeatureProviderException + /// Message to display + /// Original exception + public FlagNotFoundError(string message, Exception innerException = null) : base(ErrorType.FlagNotFound, + message, innerException) { - /// - /// Constructor of the exception - /// - /// Message to display - /// Original exception - public FlagNotFoundError(string message, Exception innerException = null) : base(ErrorType.FlagNotFound, - message, innerException) - { - } } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/GeneralError.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/GeneralError.cs index 47c0c799..d69bf8e9 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/GeneralError.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/GeneralError.cs @@ -2,21 +2,20 @@ using OpenFeature.Constant; using OpenFeature.Error; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception +namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception; + +/// +/// Exception throw when we don't have a specific case. +/// +public class GeneralError : FeatureProviderException { /// - /// Exception throw when we don't have a specific case. + /// Constructor of the exception /// - public class GeneralError : FeatureProviderException + /// Message to display + /// Original exception + public GeneralError(string message, Exception innerException = null) : base(ErrorType.General, message, + innerException) { - /// - /// Constructor of the exception - /// - /// Message to display - /// Original exception - public GeneralError(string message, Exception innerException = null) : base(ErrorType.General, message, - innerException) - { - } } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/GoFeatureFlagException.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/GoFeatureFlagException.cs index a6b87b39..681dd3bf 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/GoFeatureFlagException.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/GoFeatureFlagException.cs @@ -1,36 +1,35 @@ using System; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception +namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception; + +/// +/// GoFeatureFlagException is the root exception of GO Feature Flag provider. +/// +public abstract class GoFeatureFlagException : Exception { /// - /// GoFeatureFlagException is the root exception of GO Feature Flag provider. + /// Constructor /// - public abstract class GoFeatureFlagException : Exception + public GoFeatureFlagException() { - /// - /// Constructor - /// - public GoFeatureFlagException() - { - } + } - /// - /// Constructor - /// - /// Message of your exception - public GoFeatureFlagException(string message) - : base(message) - { - } + /// + /// Constructor + /// + /// Message of your exception + public GoFeatureFlagException(string message) + : base(message) + { + } - /// - /// Constructor - /// - /// Message of your exception - /// Root exception. - public GoFeatureFlagException(string message, Exception inner) - : base(message, inner) - { - } + /// + /// Constructor + /// + /// Message of your exception + /// Root exception. + public GoFeatureFlagException(string message, Exception inner) + : base(message, inner) + { } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/ImpossibleToConvertTypeError.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/ImpossibleToConvertTypeError.cs index c1134ba1..315763c5 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/ImpossibleToConvertTypeError.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/ImpossibleToConvertTypeError.cs @@ -2,22 +2,21 @@ using OpenFeature.Constant; using OpenFeature.Error; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception +namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception; + +/// +/// Exception throw when we have a type that we are not able to convert. +/// +public class ImpossibleToConvertTypeError : FeatureProviderException { /// - /// Exception throw when we have a type that we are not able to convert. + /// Constructor of the exception /// - public class ImpossibleToConvertTypeError : FeatureProviderException + /// Message to display + /// Original exception + public ImpossibleToConvertTypeError(string message, Exception innerException = null) : base( + ErrorType.ParseError, + message, innerException) { - /// - /// Constructor of the exception - /// - /// Message to display - /// Original exception - public ImpossibleToConvertTypeError(string message, Exception innerException = null) : base( - ErrorType.ParseError, - message, innerException) - { - } } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/InvalidEvaluationContext.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/InvalidEvaluationContext.cs index ebeb7b34..a2809f5d 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/InvalidEvaluationContext.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/InvalidEvaluationContext.cs @@ -2,21 +2,20 @@ using OpenFeature.Constant; using OpenFeature.Error; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception +namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception; + +/// +/// Exception throw when the Evaluation Context is invalid. +/// +public class InvalidEvaluationContext : FeatureProviderException { /// - /// Exception throw when the Evaluation Context is invalid. + /// Constructor of the exception /// - public class InvalidEvaluationContext : FeatureProviderException + /// Message to display + /// Original exception + public InvalidEvaluationContext(string message, Exception innerException = null) : base( + ErrorType.InvalidContext, message, innerException) { - /// - /// Constructor of the exception - /// - /// Message to display - /// Original exception - public InvalidEvaluationContext(string message, Exception innerException = null) : base( - ErrorType.InvalidContext, message, innerException) - { - } } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/InvalidOption.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/InvalidOption.cs index dc72a244..90981f82 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/InvalidOption.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/InvalidOption.cs @@ -1,16 +1,15 @@ -namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception +namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception; + +/// +/// Exception throw when the options of the provider are invalid. +/// +public class InvalidOption : GoFeatureFlagException { /// - /// Exception throw when the options of the provider are invalid. + /// Constructor of the exception /// - public class InvalidOption : GoFeatureFlagException + /// Message to display + public InvalidOption(string message) : base(message) { - /// - /// Constructor of the exception - /// - /// Message to display - public InvalidOption(string message) : base(message) - { - } } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/InvalidTargetingKey.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/InvalidTargetingKey.cs index 7f6d9299..91a1df06 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/InvalidTargetingKey.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/InvalidTargetingKey.cs @@ -2,21 +2,20 @@ using OpenFeature.Constant; using OpenFeature.Error; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception +namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception; + +/// +/// Exception throw when The Evaluation Context does not contains a targetingKey field. +/// +public class InvalidTargetingKey : FeatureProviderException { /// - /// Exception throw when The Evaluation Context does not contains a targetingKey field. + /// Constructor of the exception /// - public class InvalidTargetingKey : FeatureProviderException + /// Message to display + /// Original exception + public InvalidTargetingKey(string message, Exception innerException = null) : base(ErrorType.InvalidContext, + message, innerException) { - /// - /// Constructor of the exception - /// - /// Message to display - /// Original exception - public InvalidTargetingKey(string message, Exception innerException = null) : base(ErrorType.InvalidContext, - message, innerException) - { - } } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/TypeMismatchError.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/TypeMismatchError.cs index 75146a89..8c506766 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/TypeMismatchError.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/TypeMismatchError.cs @@ -2,21 +2,20 @@ using OpenFeature.Constant; using OpenFeature.Error; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception +namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception; + +/// +/// Exception throw when the type we received from GO Feature Flag is different than the one expected. +/// +public class TypeMismatchError : FeatureProviderException { /// - /// Exception throw when the type we received from GO Feature Flag is different than the one expected. + /// Constructor of the exception /// - public class TypeMismatchError : FeatureProviderException + /// Message to display + /// Original exception + public TypeMismatchError(string message, Exception innerException = null) : base(ErrorType.TypeMismatch, + message, innerException) { - /// - /// Constructor of the exception - /// - /// Message to display - /// Original exception - public TypeMismatchError(string message, Exception innerException = null) : base(ErrorType.TypeMismatch, - message, innerException) - { - } } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/UnauthorizedError.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/UnauthorizedError.cs index bcad6777..a995e09d 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/UnauthorizedError.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/exception/UnauthorizedError.cs @@ -2,21 +2,20 @@ using OpenFeature.Constant; using OpenFeature.Error; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception +namespace OpenFeature.Contrib.Providers.GOFeatureFlag.exception; + +/// +/// Exception throw when we are not authorized to call the API in the relay proxy. +/// +public class UnauthorizedError : FeatureProviderException { /// - /// Exception throw when we are not authorized to call the API in the relay proxy. + /// Constructor of the exception /// - public class UnauthorizedError : FeatureProviderException + /// Message to display + /// Original exception + public UnauthorizedError(string message, Exception innerException = null) : base(ErrorType.General, message, + innerException) { - /// - /// Constructor of the exception - /// - /// Message to display - /// Original exception - public UnauthorizedError(string message, Exception innerException = null) : base(ErrorType.General, message, - innerException) - { - } } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/extensions/GoFeatureFlagExtensions.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/extensions/GoFeatureFlagExtensions.cs index 8407a5cf..000e8e68 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/extensions/GoFeatureFlagExtensions.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/extensions/GoFeatureFlagExtensions.cs @@ -1,22 +1,21 @@ using System.Collections.Generic; using OpenFeature.Model; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag.extensions +namespace OpenFeature.Contrib.Providers.GOFeatureFlag.extensions; + +/// +/// Extensions for GO Feature Flag provider. +/// +public static class GoFeatureFlagExtensions { /// - /// Extensions for GO Feature Flag provider. + /// Convert a Dictionary to an ImmutableMetadata. /// - public static class GoFeatureFlagExtensions + /// + /// + public static ImmutableMetadata + ToImmutableMetadata(this Dictionary metadataDictionary) // 'this' keyword is crucial { - /// - /// Convert a Dictionary to an ImmutableMetadata. - /// - /// - /// - public static ImmutableMetadata - ToImmutableMetadata(this Dictionary metadataDictionary) // 'this' keyword is crucial - { - return metadataDictionary != null ? new ImmutableMetadata(metadataDictionary) : null; - } + return metadataDictionary != null ? new ImmutableMetadata(metadataDictionary) : null; } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/hooks/EnrichEvaluationContextHook.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/hooks/EnrichEvaluationContextHook.cs index c0498288..a0afa408 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/hooks/EnrichEvaluationContextHook.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/hooks/EnrichEvaluationContextHook.cs @@ -4,39 +4,38 @@ using OpenFeature.Contrib.Providers.GOFeatureFlag.models; using OpenFeature.Model; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag.hooks +namespace OpenFeature.Contrib.Providers.GOFeatureFlag.hooks; + +/// +/// Enrich the evaluation context with additional information +/// +public class EnrichEvaluationContextHook : Hook { + private readonly Structure _metadata; + /// - /// Enrich the evaluation context with additional information + /// Constructor of the Hook /// - public class EnrichEvaluationContextHook : Hook + /// metadata to use in order to enrich the evaluation context + public EnrichEvaluationContextHook(ExporterMetadata metadata) { - private readonly Structure _metadata; - - /// - /// Constructor of the Hook - /// - /// metadata to use in order to enrich the evaluation context - public EnrichEvaluationContextHook(ExporterMetadata metadata) - { - _metadata = metadata.AsStructure(); - } + _metadata = metadata.AsStructure(); + } - /// - /// Enrich the evaluation context with additional information before the evaluation of the flag - /// - /// - /// - /// - /// - /// - public override ValueTask BeforeAsync(HookContext context, - IReadOnlyDictionary hints = null, CancellationToken cancellationToken = default) - { - var builder = EvaluationContext.Builder(); - builder.Merge(context.EvaluationContext); - builder.Set("gofeatureflag", _metadata); - return new ValueTask(builder.Build()); - } + /// + /// Enrich the evaluation context with additional information before the evaluation of the flag + /// + /// + /// + /// + /// + /// + public override ValueTask BeforeAsync(HookContext context, + IReadOnlyDictionary hints = null, CancellationToken cancellationToken = default) + { + var builder = EvaluationContext.Builder(); + builder.Merge(context.EvaluationContext); + builder.Set("gofeatureflag", _metadata); + return new ValueTask(builder.Build()); } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/models/ExporterMetadata.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/models/ExporterMetadata.cs index 45a01b3b..05fbd137 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/models/ExporterMetadata.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/models/ExporterMetadata.cs @@ -1,61 +1,60 @@ using OpenFeature.Model; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag.models +namespace OpenFeature.Contrib.Providers.GOFeatureFlag.models; + +/// +/// This class represent the exporter metadata that will be sent in your evaluation data collectore +/// +public class ExporterMetadata { + private readonly StructureBuilder _exporterMetadataBuilder = Structure.Builder(); + /// - /// This class represent the exporter metadata that will be sent in your evaluation data collectore + /// Add metadata to the exporter /// - public class ExporterMetadata + /// + /// + public void Add(string key, string value) { - private readonly StructureBuilder _exporterMetadataBuilder = Structure.Builder(); - - /// - /// Add metadata to the exporter - /// - /// - /// - public void Add(string key, string value) - { - _exporterMetadataBuilder.Set(key, value); - } + _exporterMetadataBuilder.Set(key, value); + } - /// - /// Add metadata to the exporter - /// - /// - /// - public void Add(string key, bool value) - { - _exporterMetadataBuilder.Set(key, value); - } + /// + /// Add metadata to the exporter + /// + /// + /// + public void Add(string key, bool value) + { + _exporterMetadataBuilder.Set(key, value); + } - /// - /// Add metadata to the exporter - /// - /// - /// - public void Add(string key, double value) - { - _exporterMetadataBuilder.Set(key, value); - } + /// + /// Add metadata to the exporter + /// + /// + /// + public void Add(string key, double value) + { + _exporterMetadataBuilder.Set(key, value); + } - /// - /// Add metadata to the exporter - /// - /// - /// - public void Add(string key, int value) - { - _exporterMetadataBuilder.Set(key, value); - } + /// + /// Add metadata to the exporter + /// + /// + /// + public void Add(string key, int value) + { + _exporterMetadataBuilder.Set(key, value); + } - /// - /// Return the metadata as a structure - /// - /// - public Structure AsStructure() - { - return _exporterMetadataBuilder.Build(); - } + /// + /// Return the metadata as a structure + /// + /// + public Structure AsStructure() + { + return _exporterMetadataBuilder.Build(); } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/models/OfrepRequest.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/models/OfrepRequest.cs index 609c096c..750f6b80 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/models/OfrepRequest.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/models/OfrepRequest.cs @@ -4,47 +4,46 @@ using OpenFeature.Contrib.Providers.GOFeatureFlag.exception; using OpenFeature.Model; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag +namespace OpenFeature.Contrib.Providers.GOFeatureFlag; + +/// +/// GO Feature Flag request to be sent to the evaluation API +/// +public class OfrepRequest { + private const string KeyField = "targetingKey"; + private readonly EvaluationContext _ctx; + /// - /// GO Feature Flag request to be sent to the evaluation API + /// Create a new GO Feature Flag request to be sent to the evaluation API /// - public class OfrepRequest + /// + /// + /// + public OfrepRequest(EvaluationContext ctx) { - private const string KeyField = "targetingKey"; - private readonly EvaluationContext _ctx; - - /// - /// Create a new GO Feature Flag request to be sent to the evaluation API - /// - /// - /// - /// - public OfrepRequest(EvaluationContext ctx) + try { - 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); - } - - _ctx = ctx; + 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."); } - - /// - /// Returns the JSON request as string to be sent to the API - /// - /// JSON request as string to be sent to the API - public string AsJsonString() + catch (KeyNotFoundException e) { - var request = new Dictionary { { "context", _ctx.AsDictionary() } }; - return JsonSerializer.Serialize(request, JsonConverterExtensions.DefaultSerializerSettings); + throw new InvalidTargetingKey("targetingKey field is mandatory.", e); } + + _ctx = ctx; + } + + /// + /// Returns the JSON request as string to be sent to the API + /// + /// JSON request as string to be sent to the API + public string AsJsonString() + { + var request = new Dictionary { { "context", _ctx.AsDictionary() } }; + return JsonSerializer.Serialize(request, JsonConverterExtensions.DefaultSerializerSettings); } } diff --git a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/models/OfrepResponse.cs b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/models/OfrepResponse.cs index df232a38..505cb61d 100644 --- a/src/OpenFeature.Contrib.Providers.GOFeatureFlag/models/OfrepResponse.cs +++ b/src/OpenFeature.Contrib.Providers.GOFeatureFlag/models/OfrepResponse.cs @@ -1,50 +1,49 @@ using System.Collections.Generic; -namespace OpenFeature.Contrib.Providers.GOFeatureFlag +namespace OpenFeature.Contrib.Providers.GOFeatureFlag; + +/// +/// OfrepResponse is the response returned by the OFREP API. +/// +public class OfrepResponse { /// - /// OfrepResponse is the response returned by the OFREP API. - /// - public class OfrepResponse - { - /// - /// value contains the result of the flag. - /// - public object Value { get; set; } - - /// - /// key contains the name of the feature flag. - /// - public string Key { get; set; } - - /// - /// reason used to choose this variation. - /// - public string Reason { get; set; } - - /// - /// variationType contains the name of the variation used for this flag. - /// - public string Variant { get; set; } - - /// - /// cacheable is true if the flag is cacheable. - /// - public bool Cacheable { get; set; } - - /// - /// errorCode is empty if everything went ok. - /// - public string ErrorCode { get; set; } - - /// - /// errorDetails is set only if errorCode is not empty. - /// - public string ErrorDetails { get; set; } - - /// - /// metadata contains the metadata of the flag. - /// - public Dictionary Metadata { get; set; } - } + /// value contains the result of the flag. + /// + public object Value { get; set; } + + /// + /// key contains the name of the feature flag. + /// + public string Key { get; set; } + + /// + /// reason used to choose this variation. + /// + public string Reason { get; set; } + + /// + /// variationType contains the name of the variation used for this flag. + /// + public string Variant { get; set; } + + /// + /// cacheable is true if the flag is cacheable. + /// + public bool Cacheable { get; set; } + + /// + /// errorCode is empty if everything went ok. + /// + public string ErrorCode { get; set; } + + /// + /// errorDetails is set only if errorCode is not empty. + /// + public string ErrorDetails { get; set; } + + /// + /// metadata contains the metadata of the flag. + /// + public Dictionary Metadata { get; set; } }