Skip to content

Commit

Permalink
feat: back targetingKey with internal map (open-feature#287)
Browse files Browse the repository at this point in the history
Use the internal dictionary for the `targetingKey`.

This is non-breaking from a compiler perspective. It could result in
some behavioral changes, but IMO they are largely desirable.

Fixes: open-feature#235

---------

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: André Silva <2493377+askpt@users.noreply.github.com>
  • Loading branch information
toddbaert and askpt authored Jul 29, 2024
1 parent 2f8bd21 commit ccc2f7f
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 28 deletions.
23 changes: 17 additions & 6 deletions src/OpenFeature/Model/EvaluationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,30 @@ namespace OpenFeature.Model
/// <seealso href="https://github.com/open-feature/spec/blob/v0.5.2/specification/sections/03-evaluation-context.md">Evaluation context</seealso>
public sealed class EvaluationContext
{
/// <summary>
/// The index for the "targeting key" property when the EvaluationContext is serialized or expressed as a dictionary.
/// </summary>
internal const string TargetingKeyIndex = "targetingKey";


private readonly Structure _structure;

/// <summary>
/// Internal constructor used by the builder.
/// </summary>
/// <param name="targetingKey">The targeting key</param>
/// <param name="content">The content of the context.</param>
internal EvaluationContext(string? targetingKey, Structure content)
/// <param name="content"></param>
internal EvaluationContext(Structure content)
{
this.TargetingKey = targetingKey;
this._structure = content;
}


/// <summary>
/// Private constructor for making an empty <see cref="EvaluationContext"/>.
/// </summary>
private EvaluationContext()
{
this._structure = Structure.Empty;
this.TargetingKey = string.Empty;
}

/// <summary>
Expand Down Expand Up @@ -89,7 +93,14 @@ public IImmutableDictionary<string, Value> AsDictionary()
/// <summary>
/// Returns the targeting key for the context.
/// </summary>
public string? TargetingKey { get; }
public string? TargetingKey
{
get
{
this._structure.TryGetValue(TargetingKeyIndex, out Value? targetingKey);
return targetingKey?.AsString;
}
}

/// <summary>
/// Return an enumerator for all values
Expand Down
23 changes: 2 additions & 21 deletions src/OpenFeature/Model/EvaluationContextBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ public sealed class EvaluationContextBuilder
{
private readonly StructureBuilder _attributes = Structure.Builder();

internal string? TargetingKey { get; private set; }

/// <summary>
/// Internal to only allow direct creation by <see cref="EvaluationContext.Builder()"/>.
/// </summary>
Expand All @@ -28,7 +26,7 @@ internal EvaluationContextBuilder() { }
/// <returns>This builder</returns>
public EvaluationContextBuilder SetTargetingKey(string targetingKey)
{
this.TargetingKey = targetingKey;
this._attributes.Set(EvaluationContext.TargetingKeyIndex, targetingKey);
return this;
}

Expand Down Expand Up @@ -138,23 +136,6 @@ public EvaluationContextBuilder Set(string key, DateTime value)
/// <returns>This builder</returns>
public EvaluationContextBuilder Merge(EvaluationContext context)
{
string? newTargetingKey = "";

if (!string.IsNullOrWhiteSpace(this.TargetingKey))
{
newTargetingKey = this.TargetingKey;
}

if (!string.IsNullOrWhiteSpace(context.TargetingKey))
{
newTargetingKey = context.TargetingKey;
}

if (!string.IsNullOrWhiteSpace(newTargetingKey))
{
this.TargetingKey = newTargetingKey;
}

foreach (var kvp in context)
{
this.Set(kvp.Key, kvp.Value);
Expand All @@ -169,7 +150,7 @@ public EvaluationContextBuilder Merge(EvaluationContext context)
/// <returns>An immutable <see cref="EvaluationContext"/></returns>
public EvaluationContext Build()
{
return new EvaluationContext(this.TargetingKey, this._attributes.Build());
return new EvaluationContext(this._attributes.Build());
}
}
}
52 changes: 51 additions & 1 deletion test/OpenFeature.Tests/OpenFeatureEvaluationContextTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ public void TryGetValue_WhenCalledWithExistingKey_ReturnsTrueAndExpectedValue()
var key = "testKey";
var expectedValue = new Value("testValue");
var structure = new Structure(new Dictionary<string, Value> { { key, expectedValue } });
var evaluationContext = new EvaluationContext("targetingKey", structure);
var evaluationContext = new EvaluationContext(structure);

// Act
var result = evaluationContext.TryGetValue(key, out var actualValue);
Expand All @@ -169,5 +169,55 @@ public void TryGetValue_WhenCalledWithExistingKey_ReturnsTrueAndExpectedValue()
Assert.True(result);
Assert.Equal(expectedValue, actualValue);
}

[Fact]
public void GetValueOnTargetingKeySetWithTargetingKey_Equals_TargetingKey()
{
// Arrange
var value = "my_targeting_key";
var evaluationContext = EvaluationContext.Builder().SetTargetingKey(value).Build();

// Act
var result = evaluationContext.TryGetValue(EvaluationContext.TargetingKeyIndex, out var actualFromStructure);
var actualFromTargetingKey = evaluationContext.TargetingKey;

// Assert
Assert.True(result);
Assert.Equal(value, actualFromStructure?.AsString);
Assert.Equal(value, actualFromTargetingKey);
}

[Fact]
public void GetValueOnTargetingKeySetWithStructure_Equals_TargetingKey()
{
// Arrange
var value = "my_targeting_key";
var evaluationContext = EvaluationContext.Builder().Set(EvaluationContext.TargetingKeyIndex, new Value(value)).Build();

// Act
var result = evaluationContext.TryGetValue(EvaluationContext.TargetingKeyIndex, out var actualFromStructure);
var actualFromTargetingKey = evaluationContext.TargetingKey;

// Assert
Assert.True(result);
Assert.Equal(value, actualFromStructure?.AsString);
Assert.Equal(value, actualFromTargetingKey);
}

[Fact]
public void GetValueOnTargetingKeySetWithNonStringValue_Equals_Null()
{
// Arrange
var evaluationContext = EvaluationContext.Builder().Set(EvaluationContext.TargetingKeyIndex, new Value(1)).Build();

// Act
var result = evaluationContext.TryGetValue(EvaluationContext.TargetingKeyIndex, out var actualFromStructure);
var actualFromTargetingKey = evaluationContext.TargetingKey;

// Assert
Assert.True(result);
Assert.Null(actualFromStructure?.AsString);
Assert.Null(actualFromTargetingKey);
}
}
}

0 comments on commit ccc2f7f

Please sign in to comment.