Skip to content

Commit

Permalink
feat!: use (and require) OpenFeature SDK v2 (#262)
Browse files Browse the repository at this point in the history
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
  • Loading branch information
toddbaert authored Aug 22, 2024
1 parent 1f69529 commit f845134
Show file tree
Hide file tree
Showing 34 changed files with 296 additions and 422 deletions.
4 changes: 2 additions & 2 deletions build/Common.props
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
Please sort alphabetically.
Refer to https://docs.microsoft.com/nuget/concepts/package-versioning for semver syntax.
-->
<!-- 1.5-1.9999 -->
<OpenFeatureVer>[1.5,2.0)</OpenFeatureVer>
<!-- 2.0-2.9999 -->
<OpenFeatureVer>[2.0,3.0)</OpenFeatureVer>
</PropertyGroup>

<ItemGroup Condition="'$(OS)' == 'Unix'">
Expand Down
59 changes: 13 additions & 46 deletions src/OpenFeature.Contrib.Hooks.Otel/MetricsHook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Diagnostics;
using System.Diagnostics.Metrics;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using OpenFeature.Model;

Expand Down Expand Up @@ -36,17 +37,8 @@ public MetricsHook()
_evaluationErrorCounter = meter.CreateCounter<long>(MetricsConstants.ErrorTotalName, description: MetricsConstants.ErrorDescription);
}

/// <summary>
/// Executes before the flag evaluation and captures metrics related to the evaluation.
/// The metrics are captured in the following order:
/// 1. The active count is incremented. (feature_flag.evaluation_active_count)
/// 2. The request count is incremented. (feature_flag.evaluation_requests_total)
/// </summary>
/// <typeparam name="T">The type of the flag value.</typeparam>
/// <param name="context">The hook context.</param>
/// <param name="hints">The optional hints.</param>
/// <returns>The evaluation context.</returns>
public override Task<EvaluationContext> Before<T>(HookContext<T> context, IReadOnlyDictionary<string, object> hints = null)
/// <inheritdoc/>
public override ValueTask<EvaluationContext> BeforeAsync<T>(HookContext<T> context, IReadOnlyDictionary<string, object> hints = null, CancellationToken cancellationToken = default)
{
var tagList = new TagList
{
Expand All @@ -57,21 +49,12 @@ public override Task<EvaluationContext> Before<T>(HookContext<T> context, IReadO
_evaluationActiveUpDownCounter.Add(1, tagList);
_evaluationRequestCounter.Add(1, tagList);

return base.Before(context, hints);
return base.BeforeAsync(context, hints);
}


/// <summary>
/// Executes after the flag evaluation and captures metrics related to the evaluation.
/// The metrics are captured in the following order:
/// 1. The success count is incremented. (feature_flag.evaluation_success_total)
/// </summary>
/// <typeparam name="T">The type of the flag value.</typeparam>
/// <param name="context">The hook context.</param>
/// <param name="details">The flag evaluation details.</param>
/// <param name="hints">The optional hints.</param>
/// <returns>The evaluation context.</returns>
public override Task After<T>(HookContext<T> context, FlagEvaluationDetails<T> details, IReadOnlyDictionary<string, object> hints = null)
/// <inheritdoc/>
public override ValueTask AfterAsync<T>(HookContext<T> context, FlagEvaluationDetails<T> details, IReadOnlyDictionary<string, object> hints = null, CancellationToken cancellationToken = default)
{
var tagList = new TagList
{
Expand All @@ -83,20 +66,11 @@ public override Task After<T>(HookContext<T> context, FlagEvaluationDetails<T> d

_evaluationSuccessCounter.Add(1, tagList);

return base.After(context, details, hints);
return base.AfterAsync(context, details, hints);
}

/// <summary>
/// Executes when an error occurs during flag evaluation and captures metrics related to the error.
/// The metrics are captured in the following order:
/// 1. The error count is incremented. (feature_flag.evaluation_error_total)
/// </summary>
/// <typeparam name="T">The type of the flag value.</typeparam>
/// <param name="context">The hook context.</param>
/// <param name="error">The exception that occurred.</param>
/// <param name="hints">The optional hints.</param>
/// <returns>The evaluation context.</returns>
public override Task Error<T>(HookContext<T> context, Exception error, IReadOnlyDictionary<string, object> hints = null)
/// <inheritdoc/>
public override ValueTask ErrorAsync<T>(HookContext<T> context, Exception error, IReadOnlyDictionary<string, object> hints = null, CancellationToken cancellationToken = default)
{
var tagList = new TagList
{
Expand All @@ -107,18 +81,11 @@ public override Task Error<T>(HookContext<T> context, Exception error, IReadOnly

_evaluationErrorCounter.Add(1, tagList);

return base.Error(context, error, hints);
return base.ErrorAsync(context, error, hints);
}

/// <summary>
/// Executes after the flag evaluation is complete and captures metrics related to the evaluation.
/// The active count is decremented. (feature_flag.evaluation_active_count)
/// </summary>
/// <typeparam name="T">The type of the flag value.</typeparam>
/// <param name="context">The hook context.</param>
/// <param name="hints">The optional hints.</param>
/// <returns>The evaluation context.</returns>
public override Task Finally<T>(HookContext<T> context, IReadOnlyDictionary<string, object> hints = null)
/// <inheritdoc/>
public override ValueTask FinallyAsync<T>(HookContext<T> context, IReadOnlyDictionary<string, object> hints = null, CancellationToken cancellationToken = default)
{
var tagList = new TagList
{
Expand All @@ -128,7 +95,7 @@ public override Task Finally<T>(HookContext<T> context, IReadOnlyDictionary<stri

_evaluationActiveUpDownCounter.Add(-1, tagList);

return base.Finally(context, hints);
return base.FinallyAsync(context, hints);
}
}
}
33 changes: 11 additions & 22 deletions src/OpenFeature.Contrib.Hooks.Otel/OtelHook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System;
using System.Threading;

namespace OpenFeature.Contrib.Hooks.Otel

Expand All @@ -16,34 +17,22 @@ public class OtelHook : Hook
{
private readonly TracingHook _tracingHook = new TracingHook();

/// <summary>
/// After is executed after a feature flag has been evaluated.
/// </summary>
/// <param name="context">The hook context</param>
/// <param name="details">The result of the feature flag evaluation</param>
/// <param name="hints">Hints for the feature flag evaluation</param>
/// <returns>An awaitable Task object</returns>
public override Task After<T>(HookContext<T> context, FlagEvaluationDetails<T> details,
IReadOnlyDictionary<string, object> hints = null)
/// <inheritdoc/>
public override ValueTask AfterAsync<T>(HookContext<T> context, FlagEvaluationDetails<T> details,
IReadOnlyDictionary<string, object> hints = null, CancellationToken cancellationToken = default)
{
_tracingHook.After(context, details, hints);
_tracingHook.AfterAsync(context, details, hints);

return Task.CompletedTask;
return default;
}

/// <summary>
/// Error is executed when an error during a feature flag evaluation occured.
/// </summary>
/// <param name="context">The hook context</param>
/// <param name="error">The exception thrown by feature flag provider</param>
/// <param name="hints">Hints for the feature flag evaluation</param>
/// <returns>An awaitable Task object</returns>
public override Task Error<T>(HookContext<T> context, System.Exception error,
IReadOnlyDictionary<string, object> hints = null)
/// <inheritdoc/>
public override ValueTask ErrorAsync<T>(HookContext<T> context, System.Exception error,
IReadOnlyDictionary<string, object> hints = null, CancellationToken cancellationToken = default)
{
_tracingHook.Error(context, error, hints);
_tracingHook.ErrorAsync(context, error, hints);

return Task.CompletedTask;
return default;
}

}
Expand Down
4 changes: 2 additions & 2 deletions src/OpenFeature.Contrib.Hooks.Otel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ namespace OpenFeatureTestApp

var client = OpenFeature.Api.Instance.GetClient("my-app");

var val = client.GetBooleanValue("myBoolFlag", false, null);
var val = client.GetBooleanValueAsync("myBoolFlag", false, null);

// Print the value of the 'myBoolFlag' feature flag
System.Console.WriteLine(val.Result.ToString());
Expand Down Expand Up @@ -114,7 +114,7 @@ namespace OpenFeatureTestApp

var client = OpenFeature.Api.Instance.GetClient("my-app");

var val = client.GetBooleanValue("myBoolFlag", false, null);
var val = client.GetBooleanValueAsync("myBoolFlag", false, null);

// Print the value of the 'myBoolFlag' feature flag
System.Console.WriteLine(val.Result.ToString());
Expand Down
29 changes: 9 additions & 20 deletions src/OpenFeature.Contrib.Hooks.Otel/TracingHook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using OpenTelemetry.Trace;
using System.Threading;

namespace OpenFeature.Contrib.Hooks.Otel

Expand All @@ -13,15 +14,9 @@ namespace OpenFeature.Contrib.Hooks.Otel
public class TracingHook : Hook
{

/// <summary>
/// After is executed after a feature flag has been evaluated.
/// </summary>
/// <param name="context">The hook context</param>
/// <param name="details">The result of the feature flag evaluation</param>
/// <param name="hints">Hints for the feature flag evaluation</param>
/// <returns>An awaitable Task object</returns>
public override Task After<T>(HookContext<T> context, FlagEvaluationDetails<T> details,
IReadOnlyDictionary<string, object> hints = null)
/// <inheritdoc/>
public override ValueTask AfterAsync<T>(HookContext<T> context, FlagEvaluationDetails<T> details,
IReadOnlyDictionary<string, object> hints = null, CancellationToken cancellationToken = default)
{
Activity.Current?
.SetTag("feature_flag.key", details.FlagKey)
Expand All @@ -34,22 +29,16 @@ public override Task After<T>(HookContext<T> context, FlagEvaluationDetails<T> d
["feature_flag.provider_name"] = context.ProviderMetadata.Name
}));

return Task.CompletedTask;
return default;
}

/// <summary>
/// Error is executed when an error during a feature flag evaluation occured.
/// </summary>
/// <param name="context">The hook context</param>
/// <param name="error">The exception thrown by feature flag provider</param>
/// <param name="hints">Hints for the feature flag evaluation</param>
/// <returns>An awaitable Task object</returns>
public override Task Error<T>(HookContext<T> context, System.Exception error,
IReadOnlyDictionary<string, object> hints = null)
/// <inheritdoc/>
public override ValueTask ErrorAsync<T>(HookContext<T> context, System.Exception error,
IReadOnlyDictionary<string, object> hints = null, CancellationToken cancellationToken = default)
{
Activity.Current?.RecordException(error);

return Task.CompletedTask;
return default;
}

}
Expand Down
25 changes: 13 additions & 12 deletions src/OpenFeature.Contrib.Providers.ConfigCat/ConfigCatProvider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using ConfigCat.Client;
using ConfigCat.Client.Configuration;
Expand Down Expand Up @@ -35,34 +36,34 @@ public override Metadata GetMetadata()
}

/// <inheritdoc/>
public override Task<ResolutionDetails<bool>> ResolveBooleanValue(string flagKey, bool defaultValue, EvaluationContext context = null)
public override Task<ResolutionDetails<bool>> ResolveBooleanValueAsync(string flagKey, bool defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default)
{
return ResolveFlag(flagKey, context, defaultValue);
return ResolveFlag(flagKey, context, defaultValue, cancellationToken);
}

/// <inheritdoc/>
public override Task<ResolutionDetails<string>> ResolveStringValue(string flagKey, string defaultValue, EvaluationContext context = null)
public override Task<ResolutionDetails<string>> ResolveStringValueAsync(string flagKey, string defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default)
{
return ResolveFlag(flagKey, context, defaultValue);
return ResolveFlag(flagKey, context, defaultValue, cancellationToken);
}

/// <inheritdoc/>
public override Task<ResolutionDetails<int>> ResolveIntegerValue(string flagKey, int defaultValue, EvaluationContext context = null)
public override Task<ResolutionDetails<int>> ResolveIntegerValueAsync(string flagKey, int defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default)
{
return ResolveFlag(flagKey, context, defaultValue);
return ResolveFlag(flagKey, context, defaultValue, cancellationToken);
}

/// <inheritdoc/>
public override Task<ResolutionDetails<double>> ResolveDoubleValue(string flagKey, double defaultValue, EvaluationContext context = null)
public override Task<ResolutionDetails<double>> ResolveDoubleValueAsync(string flagKey, double defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default)
{
return ResolveFlag(flagKey, context, defaultValue);
return ResolveFlag(flagKey, context, defaultValue, cancellationToken);
}

/// <inheritdoc/>
public override async Task<ResolutionDetails<Value>> ResolveStructureValue(string flagKey, Value defaultValue, EvaluationContext context = null)
public override async Task<ResolutionDetails<Value>> ResolveStructureValueAsync(string flagKey, Value defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default)
{
var user = context?.BuildUser();
var result = await Client.GetValueDetailsAsync(flagKey, defaultValue?.AsObject, user);
var result = await Client.GetValueDetailsAsync(flagKey, defaultValue?.AsObject, user, cancellationToken);
var returnValue = result.IsDefaultValue ? defaultValue : new Value(result.Value);
var details = new ResolutionDetails<Value>(flagKey, returnValue, TranslateErrorCode(result.ErrorCode), errorMessage: result.ErrorMessage, variant: result.VariationId);
if (details.ErrorType == ErrorType.None)
Expand All @@ -73,10 +74,10 @@ public override async Task<ResolutionDetails<Value>> ResolveStructureValue(strin
throw new FeatureProviderException(details.ErrorType, details.ErrorMessage);
}

private async Task<ResolutionDetails<T>> ResolveFlag<T>(string flagKey, EvaluationContext context, T defaultValue)
private async Task<ResolutionDetails<T>> ResolveFlag<T>(string flagKey, EvaluationContext context, T defaultValue, CancellationToken cancellationToken)
{
var user = context?.BuildUser();
var result = await Client.GetValueDetailsAsync(flagKey, defaultValue, user);
var result = await Client.GetValueDetailsAsync(flagKey, defaultValue, user, cancellationToken);
var details = new ResolutionDetails<T>(flagKey, result.Value, TranslateErrorCode(result.ErrorCode), errorMessage: result.ErrorMessage, variant: result.VariationId);
if (details.ErrorType == ErrorType.None)
{
Expand Down
2 changes: 1 addition & 1 deletion src/OpenFeature.Contrib.Providers.ConfigCat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ namespace OpenFeatureTestApp

var client = OpenFeature.Api.Instance.GetClient();

var val = client.GetBooleanValue("isMyAwesomeFeatureEnabled", false);
var val = client.GetBooleanValueAsync("isMyAwesomeFeatureEnabled", false);

if(isMyAwesomeFeatureEnabled)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public FeatureManagementProvider(IConfiguration configuration, FeatureManagement
public override Metadata GetMetadata() => metadata;

/// <inheritdoc />
public override async Task<ResolutionDetails<bool>> ResolveBooleanValue(string flagKey, bool defaultValue, EvaluationContext context = null)
public override async Task<ResolutionDetails<bool>> ResolveBooleanValueAsync(string flagKey, bool defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default)
{
var variant = await Evaluate(flagKey, context, CancellationToken.None);

Expand All @@ -56,7 +56,7 @@ public override async Task<ResolutionDetails<bool>> ResolveBooleanValue(string f
}

/// <inheritdoc />
public override async Task<ResolutionDetails<double>> ResolveDoubleValue(string flagKey, double defaultValue, EvaluationContext context = null)
public override async Task<ResolutionDetails<double>> ResolveDoubleValueAsync(string flagKey, double defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default)
{
var variant = await Evaluate(flagKey, context, CancellationToken.None);

Expand All @@ -67,7 +67,7 @@ public override async Task<ResolutionDetails<double>> ResolveDoubleValue(string
}

/// <inheritdoc />
public override async Task<ResolutionDetails<int>> ResolveIntegerValue(string flagKey, int defaultValue, EvaluationContext context = null)
public override async Task<ResolutionDetails<int>> ResolveIntegerValueAsync(string flagKey, int defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default)
{
var variant = await Evaluate(flagKey, context, CancellationToken.None);

Expand All @@ -78,7 +78,7 @@ public override async Task<ResolutionDetails<int>> ResolveIntegerValue(string fl
}

/// <inheritdoc />
public override async Task<ResolutionDetails<string>> ResolveStringValue(string flagKey, string defaultValue, EvaluationContext context = null)
public override async Task<ResolutionDetails<string>> ResolveStringValueAsync(string flagKey, string defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default)
{
var variant = await Evaluate(flagKey, context, CancellationToken.None);

Expand All @@ -89,7 +89,7 @@ public override async Task<ResolutionDetails<string>> ResolveStringValue(string
}

/// <inheritdoc />
public override async Task<ResolutionDetails<Value>> ResolveStructureValue(string flagKey, Value defaultValue, EvaluationContext context = null)
public override async Task<ResolutionDetails<Value>> ResolveStructureValueAsync(string flagKey, Value defaultValue, EvaluationContext context = null, CancellationToken cancellationToken = default)
{
var variant = await Evaluate(flagKey, context, CancellationToken.None);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ namespace OpenFeatureTestApp

var client = OpenFeature.Api.Instance.GetClient();

var val = await client.GetBooleanValue("myBoolFlag", false, null);
var val = await client.GetBooleanValueAsync("myBoolFlag", false, null);

System.Console.WriteLine(val.ToString());
}
Expand Down
Loading

0 comments on commit f845134

Please sign in to comment.